Skip to content

Commit

Permalink
feat(core): OneBotMember 实现 DeleteSupport
Browse files Browse the repository at this point in the history
  • Loading branch information
ForteScarlet committed Jun 11, 2024
1 parent 2517cad commit 5def48b
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,23 @@

package love.forte.simbot.component.onebot.v11.core.actor

import love.forte.simbot.ability.DeleteOption
import love.forte.simbot.ability.DeleteSupport
import love.forte.simbot.ability.StandardDeleteOption
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.SetGroupKickApi
import love.forte.simbot.component.onebot.v11.core.bot.OneBotBot
import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt
import love.forte.simbot.definition.Member
import love.forte.simbot.message.Message
import love.forte.simbot.message.MessageContent
import love.forte.simbot.suspendrunner.ST
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
import kotlin.jvm.JvmSynthetic


/**
Expand All @@ -37,9 +44,14 @@ import kotlin.coroutines.CoroutineContext
* - 来自 Group API
* ([OneBotGroup.members])
*
* ## DeleteSupport
*
* [OneBotMember] 实现 [DeleteSupport],
* [delete] 代表试着踢出这个群成员。
*
* @author ForteScarlet
*/
public interface OneBotMember : Member {
public interface OneBotMember : Member, DeleteSupport {
/**
* 协程上下文。源自 [OneBotBot], 但是不含 [Job][kotlinx.coroutines.Job]。
*/
Expand All @@ -62,13 +74,73 @@ public interface OneBotMember : Member {
override val avatar: String
get() = qqAvatar640(id.literal)

/**
* 向此成员发送消息。
*
* @throws Throwable 任何可能在请求API时得到的异常
*/
@ST
override suspend fun send(text: String): OneBotMessageReceipt

/**
* 向此成员发送消息。
*
* @throws Throwable 任何可能在请求API时得到的异常
*/
@ST
override suspend fun send(message: Message): OneBotMessageReceipt

/**
* 向此成员发送消息。
*
* @throws Throwable 任何可能在请求API时得到的异常
*/
@ST
override suspend fun send(messageContent: MessageContent): OneBotMessageReceipt

/**
* 尝试踢出此群成员,类似于 `kick` 行为。
*
* @param options 删除时可提供的额外选项。
* 可用:
* - [StandardDeleteOption.IGNORE_ON_FAILURE]
* - [OneBotMemberDeleteOption] 的所有实现
*
* @throws Throwable 任何可能在请求API时得到的异常
*/
@JvmSynthetic
override suspend fun delete(vararg options: DeleteOption) {
StandardDeleteOption
SetGroupKickApi
}
}

/**
* 在 [OneBotMember.delete] 中可以使用的更多额外属性,
* 大多与 [SetGroupKickApi] 中的属性相关。
*
* @see OneBotMember.delete
*/
public sealed class OneBotMemberDeleteOption : DeleteOption {

/**
* 拒绝此人的加群请求,也就是踢出后将其屏蔽。
*
* @see SetGroupKickApi
*/
public data object RejectRequest : OneBotMemberDeleteOption()

public companion object {
/**
* 拒绝此人的加群请求,也就是踢出后将其屏蔽。
*
* @see SetGroupKickApi
* @see RejectRequest
*/
@get:JvmStatic
@get:JvmName("rejectRequest")
public val rejectRequest: RejectRequest
get() = RejectRequest

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@

package love.forte.simbot.component.onebot.v11.core.actor.internal

import love.forte.simbot.ability.DeleteOption
import love.forte.simbot.ability.StandardDeleteOption
import love.forte.simbot.common.id.ID
import love.forte.simbot.component.onebot.v11.core.actor.OneBotMember
import love.forte.simbot.component.onebot.v11.core.actor.OneBotMemberDeleteOption
import love.forte.simbot.component.onebot.v11.core.actor.OneBotMemberRole
import love.forte.simbot.component.onebot.v11.core.api.GetGroupMemberInfoResult
import love.forte.simbot.component.onebot.v11.core.api.SetGroupKickApi
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
Expand All @@ -34,10 +38,12 @@ 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
import kotlin.jvm.JvmInline


internal abstract class OneBotMemberImpl : OneBotMember {
protected abstract val bot: OneBotBotImpl
protected abstract val groupId: ID?

override suspend fun send(text: String): OneBotMessageReceipt {
return sendPrivateTextMsgApi(
Expand All @@ -64,11 +70,63 @@ internal abstract class OneBotMemberImpl : OneBotMember {
).requestDataBy(bot).toReceipt(bot)
}

override suspend fun delete(vararg options: DeleteOption) {
var mark = DeleteMark()
for (option in options) {
when {
mark.isFull -> break
option == StandardDeleteOption.IGNORE_ON_FAILURE -> mark = mark.ignoreFailure()
option == OneBotMemberDeleteOption.RejectRequest -> mark = mark.rejectRequest()
}
}

doDelete(mark)
}

private suspend fun doDelete(mark: DeleteMark) {
val groupId = this.groupId
?: if (mark.isIgnoreFailure) {
return
} else {
error("The group id for current member $this is unknown")
}

kotlin.runCatching {
SetGroupKickApi.create(
groupId = groupId,
userId = id,
rejectAddRequest = mark.isRejectRequest
).requestDataBy(bot)
}.onFailure { err ->
if (!mark.isIgnoreFailure) {
throw err
}
}
}

@JvmInline
internal value class DeleteMark(private val mark: Int = 0) {
fun ignoreFailure(): DeleteMark = DeleteMark(mark or IGNORE_FAILURE_MARK)
fun rejectRequest(): DeleteMark = DeleteMark(mark or REJECT_REQUEST)

val isIgnoreFailure: Boolean get() = mark and IGNORE_FAILURE_MARK != 0
val isRejectRequest: Boolean get() = mark and REJECT_REQUEST != 0
val isFull: Boolean get() = mark == FULL

private companion object {
const val IGNORE_FAILURE_MARK = 0b01
const val REJECT_REQUEST = 0b10

const val FULL = 0b11
}
}

override fun toString(): String = "OneBotMember(id=$id, bot=${bot.id})"
}

internal class OneBotMemberPrivateMessageEventSenderImpl(
private val source: PrivateMessageEvent.Sender,
override val groupId: ID?,
override val bot: OneBotBotImpl,
override val nick: String?,
override val role: OneBotMemberRole?,
Expand All @@ -78,12 +136,14 @@ internal class OneBotMemberPrivateMessageEventSenderImpl(
override val id: ID
get() = source.userId


override val name: String
get() = source.nickname
}

internal class OneBotMemberGroupMessageEventSenderImpl(
private val source: GroupMessageEvent.Sender,
override val groupId: ID?,
override val bot: OneBotBotImpl,
) : OneBotMemberImpl() {
override val coroutineContext: CoroutineContext = bot.subContext
Expand All @@ -104,21 +164,25 @@ internal class OneBotMemberGroupMessageEventSenderImpl(

internal fun PrivateMessageEvent.Sender.toMember(
bot: OneBotBotImpl,
groupId: ID? = null,
nick: String? = null,
role: OneBotMemberRole? = null,
): OneBotMemberPrivateMessageEventSenderImpl =
OneBotMemberPrivateMessageEventSenderImpl(
source = this,
groupId = groupId,
bot = bot,
nick = nick,
role = role
)

internal fun GroupMessageEvent.Sender.toMember(
bot: OneBotBotImpl,
groupId: ID,
): OneBotMemberGroupMessageEventSenderImpl =
OneBotMemberGroupMessageEventSenderImpl(
source = this,
groupId = groupId,
bot = bot,
)

Expand All @@ -131,6 +195,9 @@ internal class OneBotMemberApiResultImpl(
override val id: ID
get() = source.userId

override val groupId: ID
get() = source.groupId

override val name: String
get() = source.nickname

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ internal class OneBotNormalGroupMessageEventImpl(
) : OneBotGroupMessageEventImpl(sourceEvent, bot),
OneBotNormalGroupMessageEvent {
override suspend fun author(): OneBotMember {
return sourceEvent.sender.toMember(bot)
return sourceEvent.sender.toMember(bot, sourceEvent.groupId)
}

override fun toString(): String = eventToString("OneBotNormalGroupMessageEvent")
Expand All @@ -114,7 +114,7 @@ internal class OneBotAnonymousGroupMessageEventImpl(
) : OneBotGroupMessageEventImpl(sourceEvent, bot),
OneBotAnonymousGroupMessageEvent {
override suspend fun author(): OneBotMember {
return sourceEvent.sender.toMember(bot)
return sourceEvent.sender.toMember(bot, sourceEvent.groupId)
}

override fun toString(): String = eventToString("OneBotAnonymousGroupMessageEvent")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,11 @@ internal class OneBotGroupPrivateMessageEventImpl(
) : OneBotPrivateMessageEventImpl(sourceEvent, bot),
OneBotGroupPrivateMessageEvent {
override suspend fun content(): OneBotMember {
return sourceEvent.sender.toMember(bot)
return sourceEvent.sender.toMember(bot, groupId = null) // group id is unknown.
}

override suspend fun source(): OneBotGroup {
// TODO 额,怎么知道群号?
// 无法得知群号
throw UnsupportedOperationException("The way to get the group number from PrivateMessageEvent is unknown.")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package love.forte.simbot.component.onebot.v11.core.actor

import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.jsonPrimitive
import love.forte.simbot.common.id.UIntID.Companion.ID
import love.forte.simbot.common.id.ULongID.Companion.ID
import love.forte.simbot.component.onebot.v11.core.actor.internal.OneBotMemberImpl
import love.forte.simbot.component.onebot.v11.core.api.SetGroupKickApi
import love.forte.simbot.component.onebot.v11.core.api.requestData
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue


/**
*
* @author ForteScarlet
*/
class OneBotMemberDeleteTests {
@Test
fun oneBotMemberDeleteMarkTest() {
var mark = OneBotMemberImpl.DeleteMark()
assertFalse(mark.isRejectRequest)
assertFalse(mark.isIgnoreFailure)

mark = mark.rejectRequest()
assertTrue(mark.isRejectRequest)

mark = mark.ignoreFailure()
assertTrue(mark.isIgnoreFailure)
}

@Test
fun oneBotMemberDeleteTest() = runTest {
HttpClient(
MockEngine { reqData ->
val json = reqData.body.toByteArray().decodeToString()
val obj = Json.decodeFromString(JsonObject.serializer(), json)
val rejectRequest = obj["reject_add_request"]?.jsonPrimitive?.booleanOrNull
assertNotNull(rejectRequest)
assertTrue(rejectRequest)
respondOk("""{"retcode":0,"status":null,"data":null}""")
}
).use { client ->
SetGroupKickApi.create(ULong.MAX_VALUE.ID, UInt.MAX_VALUE.ID, rejectAddRequest = true).requestData(
client,
"127.0.0.1"
)
}

}

}

0 comments on commit 5def48b

Please sign in to comment.