Преглед на файлове

Clarify specialTitle docs and support to detect specialTitle changes (#1531)

sandtechnology преди 3 години
родител
ревизия
00ecf86094

+ 1 - 1
binary-compatibility-validator/android/api/binary-compatibility-validator-android.api

@@ -2813,7 +2813,7 @@ public final class net/mamoe/mirai/event/events/MemberPermissionChangeEvent : ne
 	public fun toString ()Ljava/lang/String;
 }
 
-public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent {
+public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
 	public final fun component1 ()Ljava/lang/String;
 	public final fun component2 ()Ljava/lang/String;
 	public final fun component3 ()Lnet/mamoe/mirai/contact/NormalMember;

+ 1 - 1
binary-compatibility-validator/api/binary-compatibility-validator.api

@@ -2813,7 +2813,7 @@ public final class net/mamoe/mirai/event/events/MemberPermissionChangeEvent : ne
 	public fun toString ()Ljava/lang/String;
 }
 
-public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent {
+public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
 	public final fun component1 ()Ljava/lang/String;
 	public final fun component2 ()Ljava/lang/String;
 	public final fun component3 ()Lnet/mamoe/mirai/contact/NormalMember;

+ 1 - 1
docs/EventList.md

@@ -85,7 +85,7 @@
 
 ##### 名片和头衔
 - 成员群名片改动: MemberCardChangeEvent
-- 成员群头衔改动: MemberSpecialTitleChangeEvent
+- 成员群特殊头衔改动: MemberSpecialTitleChangeEvent
 
 ##### 成员权限
 - 成员权限改变: MemberPermissionChangeEvent

+ 1 - 1
mirai-core-api/src/commonMain/kotlin/contact/Member.kt

@@ -57,7 +57,7 @@ public interface Member : User {
     public val nameCard: String
 
     /**
-     * 群头衔.
+     * 群特殊头衔.
      *
      * 为 [AnonymousMember] 时一定是 `"匿名"`
      *

+ 3 - 3
mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt

@@ -49,13 +49,13 @@ public interface NormalMember : Member {
     public override var nameCard: String
 
     /**
-     * 群头衔.
+     * 群特殊头衔.
      *
-     * 仅群主可以修改群头衔.
+     * 仅群主可以修改群特殊头衔.
      *
      * 在修改时将会异步上传至服务器.
      *
-     * @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
+     * @see MemberSpecialTitleChangeEvent 群特殊头衔被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
      * @throws PermissionDeniedException 无权限修改时
      */
     public override var specialTitle: String

+ 1 - 1
mirai-core-api/src/commonMain/kotlin/data/GroupInfo.kt

@@ -73,7 +73,7 @@ public interface GroupInfo {
 
     /*
     /**
-     * 机器人的头衔
+     * 机器人的特殊头衔
      */
     val botSpecialTitle: String
 

+ 4 - 2
mirai-core-api/src/commonMain/kotlin/event/events/group.kt

@@ -492,7 +492,9 @@ public data class MemberCardChangeEvent @MiraiInternalApi constructor(
 ) : GroupMemberEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent
 
 /**
- * 成员群头衔改动. 一定为群主操作
+ * 成员群特殊头衔改动. 一定为群主操作
+ *
+ * 由于服务器并不会告知特殊头衔的重置, 因此此事件在特殊头衔重置后只能由 mirai 在发现变动时才广播
  */
 public data class MemberSpecialTitleChangeEvent @MiraiInternalApi constructor(
     /**
@@ -513,7 +515,7 @@ public data class MemberSpecialTitleChangeEvent @MiraiInternalApi constructor(
      * 为 null 时则是机器人操作.
      */
     public override val operator: NormalMember?
-) : GroupMemberEvent, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
+) : GroupMemberEvent, GroupOperableEvent, AbstractEvent(), Packet, GroupMemberInfoChangeEvent
 
 // endregion
 

+ 9 - 1
mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt

@@ -16,6 +16,7 @@ import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.GroupMessageEvent
 import net.mamoe.mirai.event.events.GroupMessageSyncEvent
 import net.mamoe.mirai.event.events.MemberCardChangeEvent
+import net.mamoe.mirai.event.events.MemberSpecialTitleChangeEvent
 import net.mamoe.mirai.internal.contact.GroupImpl
 import net.mamoe.mirai.internal.contact.NormalMemberImpl
 import net.mamoe.mirai.internal.contact.info
@@ -124,6 +125,14 @@ internal class GroupMessageProcessor(
         sender.info?.castOrNull<MemberInfoImpl>()?.run {
             lastSpeakTimestamp = currentTimeSeconds().toInt()
         }
+        sender.castOrNull<NormalMemberImpl>()?.run {
+            val specialTitleNow = extraInfo?.senderTitle?.decodeToString().orEmpty()
+            if (specialTitle != specialTitleNow) {
+                //群特殊头衔 只有群主才能进行操作
+                collect(MemberSpecialTitleChangeEvent(specialTitle, specialTitleNow, this, group.owner))
+                _specialTitle = specialTitleNow
+            }
+        }
 
         if (isFromSelfAccount) {
             collect(
@@ -137,7 +146,6 @@ internal class GroupMessageProcessor(
             )
             return
         } else {
-
             broadcastNameCardChangedEventIfNecessary(sender, nameCard)
 
             collect(

+ 36 - 7
mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt

@@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.notice.group
 import kotlinx.io.core.readUInt
 import kotlinx.io.core.readUShort
 import net.mamoe.mirai.contact.NormalMember
+import net.mamoe.mirai.contact.getMember
 import net.mamoe.mirai.data.GroupHonorType
 import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.internal.QQAndroidBot
@@ -29,6 +30,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27
 import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
 import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.internal.utils.io.serialization.loadAs
+import net.mamoe.mirai.internal.utils.parseToMessageDataList
 import net.mamoe.mirai.utils.*
 
 internal class GroupNotificationProcessor(
@@ -158,8 +160,8 @@ internal class GroupNotificationProcessor(
         when (data.kind) {
             0x0C -> processMute(data)
             0x0E -> processAllowAnonymousChat(data)
-            0x10 -> processAllowConfessTask(data)
-            0x14 -> processGrayTip(data)
+            0x10 -> processNormalGrayTip(data)
+            0x14 -> processGeneralGrayTip(data)
         }
     }
 
@@ -249,8 +251,10 @@ internal class GroupNotificationProcessor(
 
     /**
      * @see GroupAllowConfessTalkEvent
+     * @see MemberSpecialTitleChangeEvent
      */
-    private fun NoticePipelineContext.processAllowConfessTask(
+    //gray tip: 聊天中的灰色小框系统提示信息(无通用模板,为混合xml代码的文本)
+    private fun NoticePipelineContext.processNormalGrayTip(
         data: MsgType0x2DC,
     ) = data.context {
         val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
@@ -260,10 +264,10 @@ internal class GroupNotificationProcessor(
                 val tipsInfo = proto.optMsgGraytips ?: return
 
                 val message = tipsInfo.optBytesContent.decodeToString()
-                // 机器人信息
                 when (tipsInfo.robotGroupOpt) {
-                    // others
+                    // 非机器人信息
                     0 -> {
+                        //坦白说开关
                         if (message.endsWith("群聊坦白说")) {
                             val new = when (message) {
                                 "管理员已关闭群聊坦白说" -> false
@@ -274,6 +278,29 @@ internal class GroupNotificationProcessor(
                                 }
                             }
                             collect(GroupAllowConfessTalkEvent(new, !new, group, false))
+                            //群特殊头衔授予
+                        } else if (message.endsWith(">头衔")) {
+                            message.parseToMessageDataList().let { seq ->
+                                if (seq.count() == 2) {
+                                    val uin = seq.first().data.toLong()
+                                    val newTitle = seq.last().text
+                                    val member = group.getMember(uin) ?: return@let
+                                    member.checkIsMemberImpl()
+                                    collect(
+                                        MemberSpecialTitleChangeEvent(
+                                            member.specialTitle,
+                                            newTitle,
+                                            member,
+                                            group.owner
+                                        )
+                                    )
+                                    member._specialTitle = newTitle
+                                } else {
+                                    logger.debug { "Unknown server special title messages $message" }
+                                    return
+                                }
+
+                            }
                         }
                     }
                 }
@@ -286,8 +313,9 @@ internal class GroupNotificationProcessor(
      * @see NudgeEvent
      * @see MemberHonorChangeEvent
      * @see GroupTalkativeChangeEvent
-     */ // gray tip: 聊天中的灰色小框系统提示信息
-    private fun NoticePipelineContext.processGrayTip(
+     */
+    // general gray tip: 聊天中的灰色小框系统提示信息(有通用模板)
+    private fun NoticePipelineContext.processGeneralGrayTip(
         data: MsgType0x2DC,
     ) = data.context {
         val grayTip = buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), 1).optGeneralGrayTip
@@ -323,6 +351,7 @@ internal class GroupNotificationProcessor(
                     collect(MemberHonorChangeEvent.Achieve(now, GroupHonorType.TALKATIVE))
                 }
             }
+            //
             else -> {
                 markNotConsumed()
                 logger.debug {

+ 2 - 1
mirai-core/src/commonMain/kotlin/utils/string.kt

@@ -36,11 +36,12 @@ internal fun MessageData.toMemberInfo() = MemberInfoImpl(
 
 @Suppress("RegExpRedundantEscape")
 internal val extraJsonPattern = Regex("<(\\{.*?\\})>")
+internal val jsonInstance = Json { ignoreUnknownKeys = true }
 
 @MiraiInternalApi
 internal fun String.parseToMessageDataList(): Sequence<MessageData> {
     return extraJsonPattern.findAll(this).filter { it.groups.size == 2 }.mapNotNull { result ->
-        Json.decodeFromString(MessageData.serializer(), result.groups[1]!!.value)
+        jsonInstance.decodeFromString(MessageData.serializer(), result.groups[1]!!.value)
     }
 }