diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotStranger.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotStranger.kt
new file mode 100644
index 0000000..3456c28
--- /dev/null
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotStranger.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.actor
+
+import love.forte.simbot.common.id.ID
+import love.forte.simbot.common.id.literal
+import love.forte.simbot.component.onebot.v11.common.utils.qqAvatar640
+import love.forte.simbot.component.onebot.v11.core.api.GetStrangerInfoApi
+import love.forte.simbot.definition.User
+
+
+/**
+ * 一个陌生人。
+ * 通常是通过 [GetStrangerInfoApi]
+ * 得到的信息。
+ *
+ * @author ForteScarlet
+ */
+public interface OneBotStranger : User {
+ override val id: ID
+ override val name: String
+
+ override val avatar: String
+ get() = qqAvatar640(id.literal)
+
+ /**
+ * 年龄。
+ * 如果无法获取则有可能会被填充一个默认值。
+ */
+ public val age: Int
+
+ /**
+ * 性别。`male` 或 `female` 或 `unknown`
+ */
+ public val sex: String
+}
diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotStrangerImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotStrangerImpl.kt
new file mode 100644
index 0000000..6ab5e74
--- /dev/null
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotStrangerImpl.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.actor.internal
+
+import love.forte.simbot.common.id.ID
+import love.forte.simbot.component.onebot.v11.core.actor.OneBotStranger
+import love.forte.simbot.component.onebot.v11.core.api.GetStrangerInfoResult
+import love.forte.simbot.component.onebot.v11.core.bot.internal.OneBotBotImpl
+import kotlin.coroutines.CoroutineContext
+
+
+/**
+ *
+ * @author ForteScarlet
+ */
+internal class OneBotStrangerImpl(
+ override val coroutineContext: CoroutineContext,
+ private val source: GetStrangerInfoResult
+) : OneBotStranger {
+
+ override val id: ID
+ get() = source.userId
+
+ override val name: String
+ get() = source.nickname
+
+ override val age: Int
+ get() = source.age
+
+ override val sex: String
+ get() = source.sex
+
+ override fun toString(): String =
+ "OneBotStranger(source=$source)"
+}
+
+internal fun GetStrangerInfoResult.toStranger(
+ bot: OneBotBotImpl,
+ coroutineContext: CoroutineContext = bot.subContext,
+): OneBotStrangerImpl =
+ OneBotStrangerImpl(coroutineContext, this)
diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/internal/OneBotBotImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/internal/OneBotBotImpl.kt
index c11c68c..55de34d 100644
--- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/internal/OneBotBotImpl.kt
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/internal/OneBotBotImpl.kt
@@ -87,6 +87,8 @@ import love.forte.simbot.component.onebot.v11.core.event.internal.message.OneBot
import love.forte.simbot.component.onebot.v11.core.event.internal.meta.OneBotDefaultMetaEventImpl
import love.forte.simbot.component.onebot.v11.core.event.internal.meta.OneBotHeartbeatEventImpl
import love.forte.simbot.component.onebot.v11.core.event.internal.meta.OneBotLifecycleEventImpl
+import love.forte.simbot.component.onebot.v11.core.event.internal.request.OneBotFriendRequestEventImpl
+import love.forte.simbot.component.onebot.v11.core.event.internal.request.OneBotGroupRequestEventImpl
import love.forte.simbot.component.onebot.v11.core.event.internal.stage.OneBotBotStartedEventImpl
import love.forte.simbot.component.onebot.v11.core.utils.onEachErrorLog
import love.forte.simbot.component.onebot.v11.event.UnknownEvent
@@ -95,6 +97,8 @@ import love.forte.simbot.component.onebot.v11.event.message.PrivateMessageEvent
import love.forte.simbot.component.onebot.v11.event.meta.HeartbeatEvent
import love.forte.simbot.component.onebot.v11.event.meta.LifecycleEvent
import love.forte.simbot.component.onebot.v11.event.meta.MetaEvent
+import love.forte.simbot.component.onebot.v11.event.request.FriendRequestEvent
+import love.forte.simbot.component.onebot.v11.event.request.GroupRequestEvent
import love.forte.simbot.component.onebot.v11.event.resolveEventSerializer
import love.forte.simbot.component.onebot.v11.event.resolveEventSubTypeFieldName
import love.forte.simbot.event.Event
@@ -455,7 +459,7 @@ internal class OneBotBotImpl(
resolveRawEvent(eventRaw)
}.getOrElse { e ->
val exMsg = "Failed to resolve raw event $eventRaw, " +
- "session and bot will be closed exceptionally"
+ "session and bot will be closed exceptionally"
val ex = IllegalStateException(
exMsg,
@@ -575,6 +579,20 @@ internal class OneBotBotImpl(
}
//endregion
+ //region 申请事件
+ is FriendRequestEvent -> OneBotFriendRequestEventImpl(
+ raw,
+ event,
+ bot
+ )
+ is GroupRequestEvent -> OneBotGroupRequestEventImpl(
+ raw,
+ event,
+ bot
+ )
+ // 其余未知的申请事件扔到 unsupported
+ //endregion
+
// TODO notice events
is UnknownEvent -> OneBotUnknownEvent(raw, event)
diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/request/OneBotRequestEventImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/request/OneBotRequestEventImpl.kt
new file mode 100644
index 0000000..e57fdfd
--- /dev/null
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/request/OneBotRequestEventImpl.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.event.internal.request
+
+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.onebot.v11.core.actor.OneBotGroup
+import love.forte.simbot.component.onebot.v11.core.actor.OneBotStranger
+import love.forte.simbot.component.onebot.v11.core.actor.internal.toStranger
+import love.forte.simbot.component.onebot.v11.core.api.GetStrangerInfoApi
+import love.forte.simbot.component.onebot.v11.core.api.SetFriendAddRequestApi
+import love.forte.simbot.component.onebot.v11.core.api.SetGroupAddRequestApi
+import love.forte.simbot.component.onebot.v11.core.bot.OneBotBot
+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.event.internal.eventToString
+import love.forte.simbot.component.onebot.v11.core.event.request.OneBotFriendRequestEvent
+import love.forte.simbot.component.onebot.v11.core.event.request.OneBotGroupRequestEvent
+import love.forte.simbot.component.onebot.v11.core.event.request.OneBotRequestEvent
+import love.forte.simbot.component.onebot.v11.core.utils.timestamp
+import love.forte.simbot.component.onebot.v11.event.request.FriendRequestEvent
+import love.forte.simbot.component.onebot.v11.event.request.GroupRequestEvent
+
+
+internal abstract class OneBotRequestEventImpl : OneBotRequestEvent {
+ override val id: ID = UUID.random()
+
+ override val time: Timestamp
+ get() = sourceEvent.timestamp()
+
+ override suspend fun accept() {
+ doSetRequest(true)
+ }
+
+ override suspend fun reject() {
+ doSetRequest(false)
+ }
+
+ protected abstract suspend fun doSetRequest(approve: Boolean)
+}
+
+internal class OneBotFriendRequestEventImpl(
+ override val sourceEventRaw: String?,
+ override val sourceEvent: FriendRequestEvent,
+ override val bot: OneBotBot,
+) : OneBotRequestEventImpl(), OneBotFriendRequestEvent {
+ override suspend fun doSetRequest(approve: Boolean) {
+ SetFriendAddRequestApi.create(
+ flag = sourceEvent.flag,
+ approve = approve,
+ ).requestDataBy(bot)
+ }
+
+ override fun toString(): String =
+ eventToString("OneBotFriendRequestEvent")
+}
+
+internal class OneBotGroupRequestEventImpl(
+ override val sourceEventRaw: String?,
+ override val sourceEvent: GroupRequestEvent,
+ override val bot: OneBotBotImpl,
+) : OneBotRequestEventImpl(), OneBotGroupRequestEvent {
+ override suspend fun doSetRequest(approve: Boolean) {
+ SetGroupAddRequestApi.create(
+ flag = sourceEvent.flag,
+ subType = sourceEvent.subType,
+ approve = approve
+ ).requestDataBy(bot)
+ }
+
+ override suspend fun content(): OneBotGroup {
+ return bot.groupRelation.group(sourceEvent.groupId)
+ ?: error("Group with id ${sourceEvent.groupId} not found")
+ }
+
+ override suspend fun requester(): OneBotStranger {
+ return GetStrangerInfoApi
+ .create(sourceEvent.userId)
+ .requestDataBy(bot)
+ .toStranger(bot)
+ }
+
+ override fun toString(): String =
+ eventToString("OneBotGroupRequestEvent")
+}
+
diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/request/OneBotRequestEvent.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/request/OneBotRequestEvent.kt
index 35fb67a..cd74d9f 100644
--- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/request/OneBotRequestEvent.kt
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/request/OneBotRequestEvent.kt
@@ -17,8 +17,15 @@
package love.forte.simbot.component.onebot.v11.core.event.request
+import love.forte.simbot.common.id.LongID
+import love.forte.simbot.component.onebot.v11.core.actor.OneBotGroup
+import love.forte.simbot.component.onebot.v11.core.actor.OneBotStranger
import love.forte.simbot.component.onebot.v11.core.event.OneBotBotEvent
+import love.forte.simbot.component.onebot.v11.event.request.FriendRequestEvent
+import love.forte.simbot.component.onebot.v11.event.request.GroupRequestEvent
+import love.forte.simbot.event.OrganizationJoinRequestEvent
import love.forte.simbot.event.RequestEvent
+import love.forte.simbot.suspendrunner.STP
public typealias OBSourceRequestEvent = love.forte.simbot.component.onebot.v11.event.request.RequestEvent
@@ -31,3 +38,97 @@ public typealias OBSourceRequestEvent = love.forte.simbot.component.onebot.v11.e
public interface OneBotRequestEvent : OneBotBotEvent, RequestEvent {
override val sourceEvent: OBSourceRequestEvent
}
+
+/**
+ * 好友添加申请事件
+ * @see FriendRequestEvent
+ */
+public interface OneBotFriendRequestEvent : OneBotRequestEvent {
+ override val sourceEvent: FriendRequestEvent
+
+ /**
+ * 好友添加申请始终是 [主动地][RequestEvent.Type.PROACTIVE]
+ */
+ override val type: RequestEvent.Type
+ get() = RequestEvent.Type.PROACTIVE
+
+ /**
+ * 验证信息。
+ *
+ * @see FriendRequestEvent.comment
+ */
+ override val message: String
+ get() = sourceEvent.comment
+
+ /**
+ * 请求 flag,在调用处理请求的 API 时需要传入
+ *
+ * @see FriendRequestEvent.flag
+ */
+ public val flag: String
+ get() = sourceEvent.flag
+
+ /**
+ * 发送请求的 QQ 号
+ *
+ * @see FriendRequestEvent.userId
+ */
+ public val requesterId: LongID
+ get() = sourceEvent.userId
+}
+
+/**
+ * 群添加申请事件
+ * @see GroupRequestEvent
+ */
+public interface OneBotGroupRequestEvent : OneBotRequestEvent, OrganizationJoinRequestEvent {
+ override val sourceEvent: GroupRequestEvent
+
+ /**
+ * 申请加入的群。
+ */
+ @STP
+ override suspend fun content(): OneBotGroup
+
+ /**
+ * 根据 [GroupRequestEvent.subType] 的值区分类型。
+ * 如果是 `invite` 则为被动,否则(包括 `add`)均视为主动。
+ *
+ */
+ override val type: RequestEvent.Type
+ get() = when (sourceEvent.subType) {
+ GroupRequestEvent.SUB_TYPE_INVITE -> RequestEvent.Type.PASSIVE
+ GroupRequestEvent.SUB_TYPE_ADD -> RequestEvent.Type.PROACTIVE
+ else -> RequestEvent.Type.PROACTIVE
+ }
+
+ /**
+ * 验证信息。
+ *
+ * @see GroupRequestEvent.comment
+ */
+ override val message: String
+ get() = sourceEvent.comment
+
+ /**
+ * 请求 flag,在调用处理请求的 API 时需要传入
+ *
+ * @see GroupRequestEvent.flag
+ */
+ public val flag: String
+ get() = sourceEvent.flag
+
+ /**
+ * 发送请求的 QQ 号
+ *
+ * @see GroupRequestEvent.userId
+ */
+ override val requesterId: LongID
+ get() = sourceEvent.userId
+
+ /**
+ * 申请者的部分信息。
+ */
+ @STP
+ override suspend fun requester(): OneBotStranger
+}
diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-event/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/event/request/GroupRequestEvent.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-event/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/event/request/GroupRequestEvent.kt
index a22ddef..032e7b9 100644
--- a/simbot-component-onebot-v11/simbot-component-onebot-v11-event/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/event/request/GroupRequestEvent.kt
+++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-event/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/event/request/GroupRequestEvent.kt
@@ -53,4 +53,17 @@ public data class GroupRequestEvent(
public val userId: LongID,
public val comment: String = "",
public val flag: String,
-) : RequestEvent
+) : RequestEvent {
+
+ public companion object {
+ /**
+ * @see GroupRequestEvent.subType
+ */
+ public const val SUB_TYPE_ADD: String = "add"
+
+ /**
+ * @see GroupRequestEvent.subType
+ */
+ public const val SUB_TYPE_INVITE: String = "invite"
+ }
+}