diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index c59dfb9ac..da65144a6 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -36,6 +36,8 @@ public abstract interface class net/mamoe/mirai/Bot : kotlinx/coroutines/Corouti public abstract fun login (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun nudge ()Lnet/mamoe/mirai/message/action/BotNudge; public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge; + public fun queryProfile (J)Lnet/mamoe/mirai/data/UserProfile; + public abstract fun queryProfile (JLkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class net/mamoe/mirai/Bot$Companion { @@ -1416,6 +1418,23 @@ public abstract interface class net/mamoe/mirai/data/UserInfo { public abstract fun getUin ()J } +public abstract interface class net/mamoe/mirai/data/UserProfile { + public abstract fun getAge ()I + public abstract fun getEmail ()Ljava/lang/String; + public abstract fun getNickname ()Ljava/lang/String; + public abstract fun getQLevel ()I + public abstract fun getSex ()Lnet/mamoe/mirai/data/UserProfile$Sex; + public abstract fun getSign ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/data/UserProfile$Sex : java/lang/Enum { + public static final field FEMALE Lnet/mamoe/mirai/data/UserProfile$Sex; + public static final field MALE Lnet/mamoe/mirai/data/UserProfile$Sex; + public static final field UNKNOWN Lnet/mamoe/mirai/data/UserProfile$Sex; + public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/data/UserProfile$Sex; + public static fun values ()[Lnet/mamoe/mirai/data/UserProfile$Sex; +} + public abstract class net/mamoe/mirai/event/AbstractEvent : net/mamoe/mirai/event/Event { public fun <init> ()V public final fun cancel ()V diff --git a/mirai-core-api/src/commonMain/kotlin/Bot.kt b/mirai-core-api/src/commonMain/kotlin/Bot.kt index 93171093a..f0af635b6 100644 --- a/mirai-core-api/src/commonMain/kotlin/Bot.kt +++ b/mirai-core-api/src/commonMain/kotlin/Bot.kt @@ -17,12 +17,14 @@ package net.mamoe.mirai import kotlinx.coroutines.* import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.contact.* +import net.mamoe.mirai.data.UserProfile import net.mamoe.mirai.event.EventChannel import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.message.action.BotNudge import net.mamoe.mirai.message.action.MemberNudge import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiLogger import java.util.concurrent.ConcurrentHashMap @@ -171,6 +173,16 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot { */ public override fun nudge(): BotNudge = BotNudge(this) + + /** + * 查询某个用户的信息 + * + * @since 2.1.0 + */ + @MiraiExperimentalApi + @JvmBlockingBridge + public suspend fun queryProfile(targetId: Long): UserProfile + /** * 关闭这个 [Bot], 立即取消 [Bot] 的 [SupervisorJob], 取消与这个 [Bot] 相关的所有有协程联系的任务. * diff --git a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt index f00e98c34..9f3222ba8 100644 --- a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt +++ b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt @@ -270,4 +270,5 @@ public interface LowLevelApiAccessor { groupId: Long, seconds: Int, ) + } diff --git a/mirai-core-api/src/commonMain/kotlin/data/UserProfile.kt b/mirai-core-api/src/commonMain/kotlin/data/UserProfile.kt new file mode 100644 index 000000000..d5122ac4d --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/data/UserProfile.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.data + +import net.mamoe.mirai.utils.MiraiExperimentalApi + +@MiraiExperimentalApi +public interface UserProfile { + public val nickname: String + public val email: String + public val age: Int + public val qLevel: Int + public val sex: Sex + + /** + * 个性签名 + */ + public val sign: String + + public enum class Sex { + MALE, + FEMALE, + /** 保密 */ + UNKNOWN; + } +} diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index 351de260a..7bb02a14b 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -26,6 +26,7 @@ import net.mamoe.mirai.internal.message.* import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.protocol.packet.chat.* +import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard import net.mamoe.mirai.message.data.* import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.utils.* @@ -110,6 +111,13 @@ internal class QQAndroidBot constructor( return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin } } + override suspend fun queryProfile(targetId: Long): UserProfile { + network.apply { + return SummaryCard.ReqSummaryCard(client, targetId) + .sendAndExpect<SummaryCard.ReqSummaryCard.RespSummaryCard>() + } + } + /** * 获取 获取群公告 所需的 bkn 参数 * */ diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt new file mode 100644 index 000000000..248d1e402 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt @@ -0,0 +1,265 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.data.jce + +import kotlinx.serialization.Serializable +import net.mamoe.mirai.internal.utils.io.JceStruct +import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId + + +@Serializable +internal class ReqHead( + @JvmField @TarsId(0) val iVersion: Int = 1 +) : JceStruct + +@Serializable +internal class ReqSummaryCard( + @JvmField @TarsId(0) val uin: Long, + @JvmField @TarsId(1) val eComeFrom: Int = 65535, + @JvmField @TarsId(2) val uQzoneFeedTimestamp: Long? = null, + @JvmField @TarsId(3) val isFriend: Byte? = null, + @JvmField @TarsId(4) val groupCode: Long? = null, + @JvmField @TarsId(5) val groupUin: Long? = null, + //@JvmField @TarsId(6) val vSeed: ByteArray? = null, + //@JvmField @TarsId(7) val searchName: String? = "", + @JvmField @TarsId(8) val getControl: Long? = null, + @JvmField @TarsId(9) val eAddFriendSource: Int? = null, + @JvmField @TarsId(10) val vSecureSig: ByteArray? = null, + //@JvmField @TarsId(11) val vReqLastGameInfo: ByteArray? = null, + //@JvmField @TarsId(12) val vReqTemplateInfo: ByteArray? = null, + //@JvmField @TarsId(13) val vReqStarInfo: ByteArray? = null, + //@JvmField @TarsId(14) val vvReqServices: List<ByteArray>? = null, + @JvmField @TarsId(15) val tinyId: Long? = null, + @JvmField @TarsId(16) val uLikeSource: Long? = null, + //@JvmField @TarsId(17) val stLocaleInfo: UserLocaleInfo? = null, + @JvmField @TarsId(18) val reqMedalWallInfo: Byte? = null, + @JvmField @TarsId(19) val vReq0x5ebFieldId: List<Int>? = null, + @JvmField @TarsId(20) val reqNearbyGodInfo: Byte? = null, + //@JvmField @TarsId(21) val reqCommLabel: Byte? = null, + @JvmField @TarsId(22) val reqExtendCard: Byte? = null, + //@JvmField @TarsId(23) val vReqKandianInfo: ByteArray? = null, + //@JvmField @TarsId(24) val uRichCardNameVer: Long? = null +) : JceStruct + +@Serializable +internal class RespHead( + @JvmField @TarsId(0) val iVersion: Int, + @JvmField @TarsId(1) val iResult: Int, + @JvmField @TarsId(2) val errorMsg: String = "", + @JvmField @TarsId(3) val vCookies: ByteArray? = null +) : JceStruct + +@Serializable +internal class RespSearch( + @JvmField @TarsId(0) val vRecords: List<SearchInfo>, + @JvmField @TarsId(1) val vSecureSig: ByteArray? = null, + @JvmField @TarsId(2) val vvRespServices: List<ByteArray>? = null +) : JceStruct + +@Serializable +internal class RespSummaryCard( +// @JvmField @TarsId(0) val iFace: Int? = null, + @JvmField @TarsId(1) val sex: Byte? = null, + @JvmField @TarsId(2) val age: Byte? = null, + @JvmField @TarsId(3) val nick: String? = "", + @JvmField @TarsId(4) val remark: String? = "", + @JvmField @TarsId(5) val iLevel: Int? = null, + @JvmField @TarsId(6) val province: String? = "", + @JvmField @TarsId(7) val city: String? = "", + @JvmField @TarsId(8) val sign: String? = "", + @JvmField @TarsId(9) val groupName: String? = "", + @JvmField @TarsId(10) val groupNick: String? = "", + @JvmField @TarsId(11) val mobile: String? = "", + @JvmField @TarsId(12) val contactName: String? = "", + @JvmField @TarsId(13) val ulShowControl: Long? = null, + @JvmField @TarsId(14) val qzoneFeedsDesc: String? = "", +// @JvmField @TarsId(15) val oLatestPhotos:AlbumInfo? = null, + @JvmField @TarsId(16) val iVoteCount: Int? = null, + @JvmField @TarsId(17) val iLastestVoteCount: Int? = null, + @JvmField @TarsId(18) val valid4Vote: Byte? = null, + @JvmField @TarsId(19) val country: String? = "", + @JvmField @TarsId(20) val status: String? = "", + @JvmField @TarsId(21) val autoRemark: String? = "", + @JvmField @TarsId(22) val cacheControl: Long? = null, + @JvmField @TarsId(23) val uin: Long? = null, + @JvmField @TarsId(24) val iPhotoCount: Int? = null, + @JvmField @TarsId(25) val eAddOption: Int? = 101, + @JvmField @TarsId(26) val vAddQuestion: List<String>? = null, + @JvmField @TarsId(27) val vSeed: ByteArray? = null, + @JvmField @TarsId(28) val discussName: String? = "", + @JvmField @TarsId(29) val stVipInfo: VipBaseInfo? = null, + @JvmField @TarsId(30) val showName: String? = "", + @JvmField @TarsId(31) val stVoiceInfo: VoiceInfo? = null, + @JvmField @TarsId(32) val vRichSign: ByteArray? = null, + @JvmField @TarsId(33) val uSignModifyTime: Long? = null, + @JvmField @TarsId(34) val vRespLastGameInfo: ByteArray? = null, + @JvmField @TarsId(35) val userFlag: Long? = null, + @JvmField @TarsId(36) val uLoginDays: Long? = null, + @JvmField @TarsId(37) val loginDesc: String? = "", + @JvmField @TarsId(38) val uTemplateId: Long? = null, + @JvmField @TarsId(39) val uQQMasterLoginDays: Long? = 20L, + @JvmField @TarsId(40) val ulFaceAddonId: Long? = null, + @JvmField @TarsId(41) val vRespTemplateInfo: ByteArray? = null, + @JvmField @TarsId(42) val respMusicInfo: String? = "", + @JvmField @TarsId(43) val vRespStarInfo: ByteArray? = null, + @JvmField @TarsId(44) val stDiamonds: VipBaseInfo? = null, + @JvmField @TarsId(45) val uAccelerateMultiple: Long? = null, + @JvmField @TarsId(46) val vvRespServices: List<ByteArray>? = null, + @JvmField @TarsId(47) val spaceName: String? = "", +// @JvmField @TarsId(48) val stDateCard:DateCard? = null, + @JvmField @TarsId(49) val iBirthday: Int? = null, +// @JvmField @TarsId(50) val stQCallInfo:QCallInfo? = null, +// @JvmField @TarsId(51) val stGiftInfo:GiftInfo? = null, +// @JvmField @TarsId(52) val stPanSocialInfo:PanSocialInfo? = null, +// @JvmField @TarsId(53) val stVideoInfo:QQVideoInfo? = null, + @JvmField @TarsId(54) val vTempChatSig: ByteArray? = null, +// @JvmField @TarsId(55) val stInterestTag:InterestTagInfo? = null, +// @JvmField @TarsId(56) val stUserFeed: UserFeed? = null, +// @JvmField @TarsId(57) val stQiqiVideoInfo:QiqiVideoInfo? = null, +// @JvmField @TarsId(58) val stPrivInfo:PrivilegeBaseInfo? = null, +// @JvmField @TarsId(59) val stApollo:QQApolloInfo? = null, +// @JvmField @TarsId(60) val stAddFrdSrcInfo:AddFrdSrcInfo? = null, +// @JvmField @TarsId(61) val stBindPhoneInfo:BindPhoneInfo? = null, + @JvmField @TarsId(62) val vVisitingCardInfo: ByteArray? = null, + @JvmField @TarsId(63) val voteLimitedNotice: String? = "", + @JvmField @TarsId(64) val haveVotedCnt: Short? = null, + @JvmField @TarsId(65) val availVoteCnt: Short? = null, + @JvmField @TarsId(66) val eIMBindPhoneNum: String? = "", + @JvmField @TarsId(67) val eIMId: String? = "", + @JvmField @TarsId(68) val email: String? = "", + @JvmField @TarsId(69) val uCareer: Long? = null, + @JvmField @TarsId(70) val personal: String? = "", + @JvmField @TarsId(71) val vHotChatInfo: ByteArray? = null, +// @JvmField @TarsId(72) val stOlympicInfo:OlympicInfo? = null, + @JvmField @TarsId(73) val stCoverInfo: TCoverInfo? = null, + @JvmField @TarsId(74) val stNowBroadcastInfo: TNowBroadcastInfo? = null, +// @JvmField @TarsId(75) val stEimInfo:TEIMInfo? = null, + @JvmField @TarsId(78) val stVideoHeadInfo: TVideoHeadInfo? = null, + @JvmField @TarsId(79) val iContactNotBindQQ: Int? = null, +// @JvmField @TarsId(80) val stMedalWallInfo:TMedalWallInfo? = null, + @JvmField @TarsId(81) val vvRespServicesBigOrder: List<ByteArray>? = null, + @JvmField @TarsId(82) val vResp0x5ebInfo: ByteArray? = null, + @JvmField @TarsId(83) val stNearbyGodInfo: TNearbyGodInfo? = null, + @JvmField @TarsId(84) val vRespQQStoryInfo: ByteArray? = null, + @JvmField @TarsId(85) val vRespCustomLabelInfo: ByteArray? = null, + @JvmField @TarsId(86) val vPraiseList: List<TPraiseInfo>? = null, + @JvmField @TarsId(87) val stCampusCircleInfo: TCampusCircleInfo? = null, + @JvmField @TarsId(88) val stTimInfo: TTimInfo? = null, + @JvmField @TarsId(89) val stQimInfo: TQimInfo? = null, +// @JvmField @TarsId(90) val stHeartInfo:HeartInfo? = null, + @JvmField @TarsId(91) val vQzoneCoverInfo: ByteArray? = null, + @JvmField @TarsId(92) val vNearbyTaskInfo: ByteArray? = null, + @JvmField @TarsId(93) val vNowInfo: ByteArray? = null, + @JvmField @TarsId(94) val uFriendGroupId: Long? = null, + @JvmField @TarsId(95) val vCommLabel: ByteArray? = null, + @JvmField @TarsId(96) val vExtendCard: ByteArray? = null, + @JvmField @TarsId(97) val qzoneHeader: String? = "", + @JvmField @TarsId(98) val mapQzoneEx: Map<String, String>? = null, + @JvmField @TarsId(99) val vRespKandianInfo: ByteArray? = null, +// @JvmField @TarsId(100) val stWeishiInfo:WeishiInfo? = null, + @JvmField @TarsId(101) val uRichCardNameVer: Long? = null, + @JvmField @TarsId(102) val uCurMulType: Long? = null, + @JvmField @TarsId(103) val vLongNickTopicInfo: ByteArray? = null +) : JceStruct + +@Serializable +internal class RespVoiceManage( + @JvmField @TarsId(0) val eOpType: Int +) : JceStruct + +@Serializable +internal class SearchInfo( + @JvmField @TarsId(0) val uIN: Long, + @JvmField @TarsId(1) val eSource: Int, + @JvmField @TarsId(2) val nick: String? = "", + @JvmField @TarsId(3) val mobile: String? = "", + @JvmField @TarsId(4) val isFriend: Byte? = null, + @JvmField @TarsId(5) val inContact: Byte? = null, + @JvmField @TarsId(6) val isEnterpriseQQ: Byte? = null +) : JceStruct + +@Serializable +internal class TCampusCircleInfo( + @JvmField @TarsId(0) val iIsSigned: Int? = null, + @JvmField @TarsId(1) val name: String? = "", + @JvmField @TarsId(2) val academy: String? = "", + @JvmField @TarsId(3) val eStatus: Int? = null, + @JvmField @TarsId(4) val stSchoolInfo: TCampusSchoolInfo? = null +) : JceStruct + +@Serializable +internal class TCampusSchoolInfo( + @JvmField @TarsId(0) val uTimestamp: Long? = null, + @JvmField @TarsId(1) val uSchoolId: Long? = null, + @JvmField @TarsId(2) val iIsValidForCertified: Int? = null +) : JceStruct + +@Serializable +internal class TCoverInfo( + @JvmField @TarsId(0) val vTagInfo: ByteArray? = null +) : JceStruct + + +@Serializable +internal class TNearbyGodInfo( + @JvmField @TarsId(0) val iIsGodFlag: Int? = null, + @JvmField @TarsId(1) val jumpUrl: String? = "" +) : JceStruct + +@Serializable +internal class TNowBroadcastInfo( + @JvmField @TarsId(0) val iFlag: Int? = null, + @JvmField @TarsId(1) val iconURL: String? = "", + @JvmField @TarsId(2) val hrefURL: String? = "", + @JvmField @TarsId(3) val vAnchorDataRsp: ByteArray? = null +) : JceStruct + +@Serializable +internal class TPraiseInfo( + @JvmField @TarsId(0) val uCustomId: Long? = null, + @JvmField @TarsId(1) val iIsPayed: Int? = null +) : JceStruct + +@Serializable +internal class TQimInfo( + @JvmField @TarsId(0) val iIsOnline: Int? = null +) : JceStruct + +@Serializable +internal class TTimInfo( + @JvmField @TarsId(0) val iIsOnline: Int? = null +) : JceStruct + +@Serializable +internal class TVideoHeadInfo( + @JvmField @TarsId(0) val iNearbyFlag: Int? = null, + @JvmField @TarsId(1) val iBasicFlag: Int? = null, + @JvmField @TarsId(2) val vMsg: ByteArray? = null +) : JceStruct + +@Serializable +internal class UserFeed( + @JvmField @TarsId(0) val uFlag: Long? = null, + @JvmField @TarsId(1) val vFeedInfo: ByteArray? = null +) : JceStruct + +@Serializable +internal class UserLocaleInfo( + @JvmField @TarsId(1) val longitude: Long? = null, + @JvmField @TarsId(2) val latitude: Long? = null +) : JceStruct + +@Serializable +internal class VoiceInfo( + @JvmField @TarsId(0) val vVoiceId: ByteArray? = null, + @JvmField @TarsId(1) val shDuration: Short? = null, + @JvmField @TarsId(2) val read: Byte? = 2, + @JvmField @TarsId(3) val url: String? = "" +) : JceStruct 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 db8824666..df64482d9 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -24,6 +24,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard import net.mamoe.mirai.internal.network.readUShortLVByteArray import net.mamoe.mirai.internal.utils.crypto.TEA import net.mamoe.mirai.internal.utils.crypto.adjustToPublicKey @@ -154,7 +155,8 @@ internal object KnownPacketFactories { NewContact.SystemMsgNewGroup, ProfileService.GroupMngReq, StrangerList.GetStrangerList, - StrangerList.DelStranger + StrangerList.DelStranger, + SummaryCard.ReqSummaryCard, ) object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/summarycard/SummaryCard.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/summarycard/SummaryCard.kt new file mode 100644 index 000000000..a2a3d59f3 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/summarycard/SummaryCard.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.summarycard + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import net.mamoe.mirai.data.UserProfile +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.network.Packet +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.protocol.data.jce.ReqHead +import net.mamoe.mirai.internal.network.protocol.data.jce.RequestDataVersion2 +import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.io.serialization.jceRequestSBuffer +import net.mamoe.mirai.internal.utils.io.serialization.readJceStruct +import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars +import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct +import net.mamoe.mirai.internal.utils.soutv +import net.mamoe.mirai.utils.read +import net.mamoe.mirai.internal.network.protocol.data.jce.ReqSummaryCard as JceReqSummaryCard +import net.mamoe.mirai.internal.network.protocol.data.jce.RespSummaryCard as JceRespSummaryCard + +internal object SummaryCard { + internal object ReqSummaryCard : OutgoingPacketFactory<ReqSummaryCard.RespSummaryCard>( + "SummaryCard.ReqSummaryCard" + ) { + internal class RespSummaryCard( + override val nickname: String, + override val email: String, + override val age: Int, + override val qLevel: Int, + override val sex: UserProfile.Sex, + override val sign: String, + ) : Packet, UserProfile { + override fun toString(): String { + return "SummaryCard.RespSummaryCard(nickname=$nickname, email=$email, age=$age, qLevel=$qLevel, sex=$sex, sign=$sign)" + } + } + + operator fun invoke( + client: QQAndroidClient, + uin: Long, + ): OutgoingPacket = buildOutgoingUniPacket(client) { + writeJceStruct( + RequestPacket.serializer(), + RequestPacket( + funcName = "ReqSummaryCard", + servantName = "SummaryCardServantObj", + version = 3, + sBuffer = jceRequestSBuffer { + "ReqHead"(ReqHead.serializer(), ReqHead(2)) + "ReqSummaryCard"( + JceReqSummaryCard.serializer(), JceReqSummaryCard( + uin = uin, + eComeFrom = 31, + getControl = 69181, + eAddFriendSource = 3001, + vSecureSig = byteArrayOf(0x00), + reqMedalWallInfo = 0, + vReq0x5ebFieldId = listOf( + 27225, 27224, 42122, 42121, 27236, + 27238, 42167, 42172, 40324, 42284, + 42326, 42325, 42356, 42363, 42361, + 42367, 42377, 42425, 42505, 42488 + ), + reqNearbyGodInfo = 1, + reqExtendCard = 1, + ) + ) + } + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): RespSummaryCard { + val fullMap = readJceStruct( + RequestPacket.serializer() + ).sBuffer.read { + readJceStruct(RequestDataVersion2.serializer()) + }.map + val map = fullMap["RespSummaryCard"] ?: error("Missing RespSummaryCard in response") + val pck = map["SummaryCard.RespSummaryCard"] + ?: map["SummaryCard_Old.RespSummaryCard"] + ?: error("No Response found") + val response = pck.read { + discardExact(1) + Tars.UTF_8.load(JceRespSummaryCard.serializer(), this) + } + return RespSummaryCard( + nickname = response.nick ?: "", + email = response.email ?: "", + age = response.age?.let { it.toInt() and 0xFF } ?: -1, + qLevel = response.iLevel ?: -1, + sex = when (response.sex?.let { it.toInt() and 0xFF }) { + 0 -> UserProfile.Sex.MALE + 1 -> UserProfile.Sex.FEMALE + else -> UserProfile.Sex.UNKNOWN + }, + sign = response.sign ?: "" + ) + } + } +}