From e62998121862709688c6c6d3b97662dfc57c3fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B1=A0?= <98259561+whitechi73@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:56:30 +0800 Subject: [PATCH] `Shamrock`: support `/get_guild_channel_list` --- .../protobuf/oidb/cmx0xf57/Oidb0xf57.kt | 11 ++ .../mobileqq/qqguildsdk/api/IGPSService.java | 2 +- .../fuqiuluo/qqinterface/servlet/GProSvc.kt | 101 +++++++++++++++--- .../qqinterface/servlet/structures/Guild.kt | 31 ++++++ .../action/handlers/GetGProChannelList.kt | 35 ++++++ .../remote/action/handlers/GetGuildList.kt | 8 +- .../action/handlers/GetGuildMetaByGuest.kt | 5 +- .../shamrock/remote/api/GuildAction.kt | 12 ++- 8 files changed, 183 insertions(+), 22 deletions(-) create mode 100644 xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGProChannelList.kt diff --git a/protobuf/src/main/java/moe/whitechi73/protobuf/oidb/cmx0xf57/Oidb0xf57.kt b/protobuf/src/main/java/moe/whitechi73/protobuf/oidb/cmx0xf57/Oidb0xf57.kt index 174daa2..133bb2f 100644 --- a/protobuf/src/main/java/moe/whitechi73/protobuf/oidb/cmx0xf57/Oidb0xf57.kt +++ b/protobuf/src/main/java/moe/whitechi73/protobuf/oidb/cmx0xf57/Oidb0xf57.kt @@ -37,6 +37,7 @@ data class Oidb0xf57Meta( @ProtoNumber(18) val ownerId: ULong = ULong.MIN_VALUE, @ProtoNumber(19) val coverSeq: Long = Long.MIN_VALUE, @ProtoNumber(20) val clientId: Int = Int.MIN_VALUE, + @ProtoNumber(27) val displayId: String? = null, ) @Serializable @@ -72,7 +73,10 @@ data class Oidb0xf57U1( @ProtoNumber(5003) val u18: UInt = UInt.MIN_VALUE, @ProtoNumber(5004) val u19: UInt = UInt.MIN_VALUE, @ProtoNumber(5005) val u20: UInt = UInt.MIN_VALUE, + @ProtoNumber(5006) val u23: UInt = UInt.MIN_VALUE, @ProtoNumber(10007) val u21: UInt = UInt.MIN_VALUE, + @ProtoNumber(15) val u22: UInt = UInt.MIN_VALUE, + @ProtoNumber(30001) val u24: UInt = UInt.MIN_VALUE, ) @Serializable @@ -85,4 +89,11 @@ data class Oidb0xf57U2( @ProtoNumber(15) val u6: UInt = UInt.MIN_VALUE, @ProtoNumber(16) val u7: UInt = UInt.MIN_VALUE, @ProtoNumber(17) val u8: UInt = UInt.MIN_VALUE, + @ProtoNumber(32) val u9: UInt = UInt.MIN_VALUE, + @ProtoNumber(31) val u10: UInt = UInt.MIN_VALUE, + @ProtoNumber(29) val u11: UInt = UInt.MIN_VALUE, + @ProtoNumber(26) val u12: UInt = UInt.MIN_VALUE, + @ProtoNumber(23) val u13: UInt = UInt.MIN_VALUE, + @ProtoNumber(22) val u14: UInt = UInt.MIN_VALUE, + @ProtoNumber(18) val u15: UInt = UInt.MIN_VALUE, ) diff --git a/qqinterface/src/main/java/com/tencent/mobileqq/qqguildsdk/api/IGPSService.java b/qqinterface/src/main/java/com/tencent/mobileqq/qqguildsdk/api/IGPSService.java index 545ee92..a9eac16 100644 --- a/qqinterface/src/main/java/com/tencent/mobileqq/qqguildsdk/api/IGPSService.java +++ b/qqinterface/src/main/java/com/tencent/mobileqq/qqguildsdk/api/IGPSService.java @@ -440,7 +440,7 @@ public interface IGPSService extends IRuntimeService { void getChannelInvisibleRoleList(String str, String str2, @NonNull com.tencent.mobileqq.qqguildsdk.data.type.e eVar);*/ @NonNull - List getChannelList(String str); + List getChannelList(String str); /* @Nullable diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt index 46811ac..753d8d1 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt @@ -4,14 +4,15 @@ package moe.fuqiuluo.qqinterface.servlet import com.tencent.mobileqq.qqguildsdk.api.IGPSService import com.tencent.qqnt.kernel.nativeinterface.GProRoleMemberList -import com.tencent.qqnt.kernel.nativeinterface.GProUser -import com.tencent.qqnt.kernel.nativeinterface.IGProGetUserInfoCallback import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.protobuf.ProtoBuf +import moe.fuqiuluo.qqinterface.servlet.structures.GProChannelInfo import moe.fuqiuluo.qqinterface.servlet.structures.GuildInfo import moe.fuqiuluo.qqinterface.servlet.structures.GuildStatus +import moe.fuqiuluo.qqinterface.servlet.structures.SlowModeInfo +import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.utils.PlatformUtils @@ -22,13 +23,11 @@ import moe.whitechi73.protobuf.oidb.cmd0xf88.Oidb0xf88Req import moe.whitechi73.protobuf.oidb.cmd0xf88.Oidb0xf88Rsp import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Filter import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57GuildInfo -import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Meta import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57MetaInfo import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Req import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Rsp import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U1 import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U2 -import tencent.im.oidb.cmd0xeac.oidb_0xeac import tencent.im.oidb.oidb_sso internal object GProSvc: BaseSvc() { @@ -40,8 +39,8 @@ internal object GProSvc: BaseSvc() { suspend fun getGuildInfo(guildId: ULong): Result { val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf57_9", 0xf57, 9, ProtoBuf.encodeToByteArray(Oidb0xf57Req( filter = Oidb0xf57Filter( - u1 = Oidb0xf57U1(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u), - u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u) + u1 = Oidb0xf57U1(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u), + u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u) ), guildInfo = Oidb0xf57GuildInfo(guildId = guildId) ))) @@ -57,10 +56,50 @@ internal object GProSvc: BaseSvc() { } } + fun getChannelList(guildId: ULong, refresh: Boolean = false): Result> { + if (refresh) { + refreshGuildInfo(guildId) + } + val result = arrayListOf() + app.getRuntimeService(IGPSService::class.java, "all").getChannelList(guildId.toString()).forEach { + result.add(GProChannelInfo( + ownerGuildId = guildId, + guildId = it.guildId, + channelId = it.channelUin.toLong(), + channelUin = it.channelUin.toLong(), + channelName = it.channelName ?: "", + channelType = it.type, + createTime = it.createTime, + creatorTinyId = it.creatorId.toLong(), + talkPermission = it.talkPermission, + visibleType = it.visibleType, + currentSlowMode = it.slowModeKey, + slowModes = it.gProSlowModeInfoList.map { + SlowModeInfo(it.slowModeKey, it.slowModeText, it.speakFrequency, it.slowModeCircle) + }, + appIconUrl = it.iconUrl, + jumpType = it.appChannelJumpType, + jumpSwitch = it.jumpSwitch, + jumpUrl = it.appChannelJumpUrl, + categoryId = it.categoryId, + myTalkPermission = it.myTalkPermissionType, + maxMemberCount = it.channelMemberMax + )) + } + return Result.success(result) + } + + fun refreshGuildInfo(guildId: ULong) { + val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService + kernelGProService.refreshGuildInfo(guildId.toLong(), true, 1) + } + suspend fun getGuildMemberList(guildId: ULong): Result> { val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService //kernelGProService.fetchMemberListWithRole() + + return Result.failure(Exception("todo")) } @@ -91,18 +130,27 @@ internal object GProSvc: BaseSvc() { } } - fun getGuildList(refresh: Boolean = false): ArrayList { - PlatformUtils.requireMinQQVersion(version = PlatformUtils.QQ_9_0_8_VER) - - val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService - if (refresh) { - kernelGProService.refreshGuildList(true) - kernelGProService.guildListFromCache.forEach { - kernelGProService.refreshGuildInfo(it.guildId, true, 1) - } + private fun getGuildListByOldApi(result: ArrayList) { + app.getRuntimeService(IGPSService::class.java, "all").guildList?.forEach { + result.add(GuildInfo( + guildId = it.guildID.toLong(), + guildName = it.guildName ?: "", + guildDisplayId = it.guildNumber ?: "", + profile = it.profile ?: "", + status = GuildStatus( + isEnable = !it.isFrozen && !it.isBanned, + isBanned = it.isBanned, + isFrozen = it.isFrozen + ), + ownerId = 0, + shutUpTime = it.shutUpExpireTime, + allowSearch = it.allowSearch + )) } + } - val result = arrayListOf() + private fun getGuildListByNt(result: ArrayList) { + val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService kernelGProService.guildListFromCache.forEach { if (it.result != 0) return@forEach val guildInfo = it.guildInfo @@ -121,6 +169,27 @@ internal object GProSvc: BaseSvc() { allowSearch = guildInfo.allowSearch == 1 )) } + } + + fun getGuildList(refresh: Boolean = false, forceOldApi: Boolean): ArrayList { + val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService + if (refresh) { + kernelGProService.refreshGuildList(true) + kernelGProService.guildListFromCache.forEach { + refreshGuildInfo(it.guildId.toULong()) + } + } + val result = arrayListOf() + if (PlatformUtils.getQQVersionCode() < PlatformUtils.QQ_9_0_8_VER || forceOldApi) { + getGuildListByOldApi(result) + } else { + runCatching { + getGuildListByNt(result) + }.onFailure { + LogCenter.log("GetGuildListByNt failed: ${it.stackTraceToString()}", Level.ERROR) + getGuildListByOldApi(result) // 防止QQ更新API导致无法获取 + } + } return result } diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/Guild.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/Guild.kt index de64027..acf31dc 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/Guild.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/Guild.kt @@ -20,4 +20,35 @@ data class GuildStatus( @SerialName("is_enable") var isEnable: Boolean, @SerialName("is_banned") var isBanned: Boolean, @SerialName("is_frozen") var isFrozen: Boolean +) + +@Serializable +data class GProChannelInfo( + @SerialName("owner_guild_id") val ownerGuildId: ULong, + @SerialName("channel_id") val channelId: Long, + @SerialName("channel_uin") val channelUin: Long, + @SerialName("guild_id") val guildId: String, + @SerialName("channel_type") val channelType: Int, + @SerialName("channel_name") val channelName: String, + @SerialName("create_time") val createTime: Long, + @SerialName("max_member_count") val maxMemberCount: Int, + @SerialName("creator_tiny_id") val creatorTinyId: Long, + @SerialName("talk_permission") val talkPermission: Int, + @SerialName("visible_type") val visibleType: Int, + @SerialName("current_slow_mode") val currentSlowMode: Int, + @SerialName("slow_modes") val slowModes: List, + @SerialName("icon_url") val appIconUrl: String? = null, + @SerialName("jump_switch") val jumpSwitch: Int = Int.MIN_VALUE, + @SerialName("jump_type") val jumpType: Int = Int.MIN_VALUE, + @SerialName("jump_url") val jumpUrl: String? = null, + @SerialName("category_id") val categoryId: Long = Long.MIN_VALUE, + @SerialName("my_talk_permission") val myTalkPermission: Int = Int.MIN_VALUE, +) + +@Serializable +data class SlowModeInfo( + @SerialName("slow_mode_key") val slowModeKey: Int, + @SerialName("slow_mode_text") val slowModeText: String, + @SerialName("speak_frequency") val speakFrequency: Int, + @SerialName("slow_mode_circle") val slowModeCircle: Int ) \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGProChannelList.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGProChannelList.kt new file mode 100644 index 0000000..25d2cd1 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGProChannelList.kt @@ -0,0 +1,35 @@ +package moe.fuqiuluo.shamrock.remote.action.handlers + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import moe.fuqiuluo.qqinterface.servlet.GProSvc +import moe.fuqiuluo.qqinterface.servlet.structures.GProChannelInfo +import moe.fuqiuluo.shamrock.remote.action.ActionSession +import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.tools.EmptyJsonString +import moe.fuqiuluo.symbols.OneBotHandler + +@OneBotHandler("get_guild_channel_list") +internal object GetGProChannelList: IActionHandler() { + override suspend fun internalHandle(session: ActionSession): String { + val guildId = session.getString("guild_id") + val refresh = session.getBooleanOrDefault("refresh", session.getBooleanOrDefault("no_cache", false)) + return invoke(guildId.toULong(), refresh, echo = session.echo) + } + + operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String { + val result = GProSvc.getChannelList(guildId, refresh) + result.onFailure { + return error(it.message ?: "unable to fetch channel list", echo) + } + return ok(GuildChannelListResult(result.getOrThrow()), echo, "success") + } + + override val requiredParams: Array = arrayOf("guild_id") + + @Serializable + data class GuildChannelListResult( + @SerialName("channel_list") val channelList: List + ) +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildList.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildList.kt index d743667..91f4b94 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildList.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildList.kt @@ -18,11 +18,13 @@ import mqq.app.MobileQQ @OneBotHandler("get_guild_list") internal object GetGuildList : IActionHandler() { override suspend fun internalHandle(session: ActionSession): String { - return invoke(echo = session.echo) + val oldSdk = session.getBooleanOrDefault("old_sdk", false) + val refresh = session.getBooleanOrDefault("refresh", session.getBooleanOrDefault("no_cache", false)) + return invoke(refresh, oldSdk, echo = session.echo) } - operator fun invoke(refresh: Boolean = true, echo: JsonElement = EmptyJsonString): String { - val result = GProSvc.getGuildList(refresh) + operator fun invoke(refresh: Boolean = true, oldSdk: Boolean, echo: JsonElement = EmptyJsonString): String { + val result = GProSvc.getGuildList(refresh, oldSdk) return ok(GuildListResult(result), echo, "success") } diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMetaByGuest.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMetaByGuest.kt index 4fcaa22..b7d0898 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMetaByGuest.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMetaByGuest.kt @@ -3,6 +3,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.protobuf.ProtoNumber import moe.fuqiuluo.qqinterface.servlet.GProSvc import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler @@ -35,7 +36,8 @@ internal object GetGuildMetaByGuest: IActionHandler() { maxRobotCount = meta.robotMaxNum, maxAdminCount = meta.adminMaxNum, memberCount = meta.memberCount, - ownerId = meta.ownerId + ownerId = meta.ownerId, + guildDisplayId = meta.displayId ?: "" ), echo) } @@ -52,5 +54,6 @@ internal object GetGuildMetaByGuest: IActionHandler() { @SerialName("max_admin_count") val maxAdminCount: Int, @SerialName("member_count") val memberCount: Long, @SerialName("owner_id") val ownerId: ULong, + @SerialName("guild_display_id") val guildDisplayId: String ) } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GuildAction.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GuildAction.kt index 95b854b..f56fa90 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GuildAction.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GuildAction.kt @@ -4,10 +4,13 @@ import io.ktor.http.ContentType import io.ktor.server.application.call import io.ktor.server.response.respondText import io.ktor.server.routing.Routing +import moe.fuqiuluo.shamrock.remote.action.handlers.GetGProChannelList import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildList import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberList import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMetaByGuest import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildServiceProfile +import moe.fuqiuluo.shamrock.tools.fetchGetOrNull +import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.getOrPost @@ -17,7 +20,8 @@ fun Routing.guildAction() { } getOrPost("/get_guild_list") { - call.respondText(GetGuildList(), ContentType.Application.Json) + val refresh = fetchGetOrNull("refresh") ?: fetchOrNull("no_cache") + call.respondText(GetGuildList(refresh?.toBoolean() ?: false, false), ContentType.Application.Json) } getOrPost("/get_guild_member_list") { @@ -29,4 +33,10 @@ fun Routing.guildAction() { val guildId = fetchOrThrow("guild_id") call.respondText(GetGuildMetaByGuest(guildId.toULong()), ContentType.Application.Json) } + + getOrPost("/get_guild_channel_list") { + val guildId = fetchOrThrow("guild_id") + val refresh = fetchGetOrNull("refresh") ?: fetchOrNull("no_cache") + call.respondText(GetGProChannelList(guildId.toULong(), refresh?.toBoolean() ?: false), ContentType.Application.Json) + } } \ No newline at end of file