diff --git a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api index eb412b6fc..62eb9a578 100644 --- a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api +++ b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api @@ -484,6 +484,9 @@ public abstract interface class net/mamoe/mirai/contact/NormalMember : net/mamoe public synthetic fun kick (Ljava/lang/String;)Lkotlin/Unit; public fun kick (Ljava/lang/String;)V public abstract fun kick (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public synthetic fun modifyAdmin (Z)Lkotlin/Unit; + public fun modifyAdmin (Z)V + public abstract fun modifyAdmin (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun nudge ()Lnet/mamoe/mirai/message/action/MemberNudge; public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge; public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge; diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index e145bd15f..7def6a2d2 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -484,6 +484,9 @@ public abstract interface class net/mamoe/mirai/contact/NormalMember : net/mamoe public synthetic fun kick (Ljava/lang/String;)Lkotlin/Unit; public fun kick (Ljava/lang/String;)V public abstract fun kick (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public synthetic fun modifyAdmin (Z)Lkotlin/Unit; + public fun modifyAdmin (Z)V + public abstract fun modifyAdmin (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun nudge ()Lnet/mamoe/mirai/message/action/MemberNudge; public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge; public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge; diff --git a/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt b/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt index 55fdf4a2a..e47892cea 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt @@ -108,6 +108,18 @@ public interface NormalMember : Member { */ public suspend fun kick(message: String) + /** + * 给予或移除群成员的管理员权限。 + * + * 此操作需要 Bot 为群主 [MemberPermission.OWNER] + * + * @param operation true 为给予 + * + * @see MemberPermissionChangeEvent 群成员权限变更事件 + * @throws PermissionDeniedException 无权限修改时抛出 + */ + public suspend fun modifyAdmin(operation: Boolean) + /** * 向群成员发送消息. * 若群成员同时是好友, 则会发送好友消息. 否则发送临时会话消息. diff --git a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt index 9b9efe668..80aff92f3 100644 --- a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.* import net.mamoe.mirai.LowLevelApi import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.MemberInfo -import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* import net.mamoe.mirai.internal.message.OnlineMessageSourceToTempImpl import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement @@ -46,7 +45,7 @@ internal class NormalMemberImpl constructor( override fun toString(): String = "NormalMember($id)" - private val handler by lazy { GroupTempSendMessageHandler(this) } + private val handler: GroupTempSendMessageHandler by lazy { GroupTempSendMessageHandler(this) } @Suppress("DuplicatedCode") override suspend fun sendMessage(message: Message): MessageReceipt<NormalMember> { @@ -183,6 +182,43 @@ internal class NormalMemberImpl constructor( MemberLeaveEvent.Kick(this@NormalMemberImpl, null).broadcastWithBot(bot) } } + + override suspend fun modifyAdmin(operation: Boolean) { + checkBotPermissionHighest("modifyAdmin") + + val origin = this@NormalMemberImpl.permission + val new = if (operation) { + MemberPermission.ADMINISTRATOR + } else { + MemberPermission.MEMBER + } + + if (origin == new) return + + bot.network.run { + val resp: TroopManagement.ModifyAdmin.Response = TroopManagement.ModifyAdmin( + client = bot.client, + member = this@NormalMemberImpl, + operation = operation + ).sendAndExpect() + + check(resp.success) { + "Failed to modify admin, cause: ${resp.msg}" + } + + this@NormalMemberImpl.permission = new + + MemberPermissionChangeEvent(this@NormalMemberImpl, origin, new).broadcastWithBot(bot) + } + } +} + +internal fun Member.checkBotPermissionHighest(operationName: String) { + check(group.botPermission == MemberPermission.OWNER) { + throw PermissionDeniedException( + "`$operationName` operation requires the OWNER permission, while bot has ${group.botPermission}" + ) + } } internal fun Member.checkBotPermissionHigherThanThis(operationName: String) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index 8218c853e..c83611f92 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -145,6 +145,7 @@ internal object KnownPacketFactories { TroopManagement.Mute, TroopManagement.GroupOperation, TroopManagement.GetTroopConfig, + TroopManagement.ModifyAdmin, // TroopManagement.GetGroupInfo, TroopManagement.EditGroupNametag, TroopManagement.Kick, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt index fb42c0986..cac566716 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt @@ -30,7 +30,6 @@ import net.mamoe.mirai.internal.utils.io.serialization.* import net.mamoe.mirai.utils.daysToSeconds internal class TroopManagement { - internal object Mute : OutgoingPacketFactory<Mute.Response>("OidbSvc.0x570_8") { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { //屁用没有 @@ -377,4 +376,47 @@ internal class TroopManagement { } } + + internal object ModifyAdmin : OutgoingPacketFactory<ModifyAdmin.Response>("OidbSvc.0x55c_1") { + data class Response(val success: Boolean, val msg: String) : Packet { + override fun toString(): String { + return "TroopManagement.ModifyAdmin.Response(success=${success}, msg=${msg})" + } + } + + /** + * @param operation: true is add + */ + operator fun invoke( + client: QQAndroidClient, + member: Member, + operation: Boolean + ): OutgoingPacket { + return buildOutgoingUniPacket(client) { + writeProtoBuf( + OidbSso.OIDBSSOPkg.serializer(), + OidbSso.OIDBSSOPkg( + command = 1372, + serviceType = 1, + bodybuffer = buildPacket { + writeInt(member.group.id.toInt()) + writeInt(member.id.toInt()) + writeByte(if (operation) 1 else 0) + }.readBytes() + ) + ) + } + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): ModifyAdmin.Response { + val stupidPacket = readProtoBuf(OidbSso.OIDBSSOPkg.serializer()) + return stupidPacket.run { + ModifyAdmin.Response( + this.result == 0, + this.errorMsg + ) + } + } + + } } \ No newline at end of file