diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GProRoleMemberList.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GProRoleMemberList.java index c9fcf1e..6ceb3a9 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GProRoleMemberList.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GProRoleMemberList.java @@ -24,8 +24,6 @@ public final class GProRoleMemberList { } public GProRoleMemberList(GProGuildRole gProGuildRole, ArrayList arrayList) { - this.role = new GProGuildRole(); - this.members = new ArrayList<>(); this.role = gProGuildRole; this.members = arrayList; } diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IGProFetchMemberListWithRoleCallback.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IGProFetchMemberListWithRoleCallback.java index 4637860..d37d50f 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IGProFetchMemberListWithRoleCallback.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IGProFetchMemberListWithRoleCallback.java @@ -3,5 +3,5 @@ package com.tencent.qqnt.kernel.nativeinterface; import java.util.ArrayList; public interface IGProFetchMemberListWithRoleCallback { - void onFetchMemberListWithRoleCallback(int result, String reason, boolean finish, long nextIndex, long nextRoleIdIndex, boolean isSmallGuild, int u, ArrayList roleList); + void onFetchMemberListWithRoleCallback(int result, String reason, boolean finish, long nextIndex, long nextRoleIdIndex, boolean isSmallGuild, int seq, ArrayList roleList); } 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 753d8d1..13a9d16 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GProSvc.kt @@ -4,11 +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.IGProFetchMemberListWithRoleCallback +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeoutOrNull 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.GetGuildMemberListNextToken import moe.fuqiuluo.qqinterface.servlet.structures.GuildInfo import moe.fuqiuluo.qqinterface.servlet.structures.GuildStatus import moe.fuqiuluo.qqinterface.servlet.structures.SlowModeInfo @@ -29,6 +33,7 @@ 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.oidb_sso +import kotlin.coroutines.resume internal object GProSvc: BaseSvc() { fun getSelfTinyId(): ULong { @@ -94,13 +99,40 @@ internal object GProSvc: BaseSvc() { kernelGProService.refreshGuildInfo(guildId.toLong(), true, 1) } - suspend fun getGuildMemberList(guildId: ULong): Result> { + suspend fun getGuildMemberList( + guildId: ULong, + startIndex: Long = 0, + roleIndex: Long = 1, + count: Int = 50, + fetchAll: Boolean = false, + result: ArrayList = arrayListOf() + ): Result>> { val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService - //kernelGProService.fetchMemberListWithRole() + val fetchGuildMemberListResult: Pair> = (withTimeoutOrNull(5000) { + suspendCancellableCoroutine { + kernelGProService.fetchMemberListWithRole(guildId.toLong(), 0, startIndex, roleIndex, count, 0) { code, reason, finish, nextIndex, nextRoleIdIndex, _, seq, roleList -> + if (code == 0) { + it.resume(GetGuildMemberListNextToken(nextIndex, nextRoleIdIndex, seq, finish) to roleList) + } else { + LogCenter.log("fetchMemberListWithRole failed: $code($reason)", Level.WARN) + it.resume(null) + } + } + } + }) ?: return Result.failure(Exception("unable to fetch guild member list")) - - - return Result.failure(Exception("todo")) + val nextToken = fetchGuildMemberListResult.first + val roleList = fetchGuildMemberListResult.second + result.addAll(roleList) + return if (fetchAll) { + if (!fetchGuildMemberListResult.first.finish) { + getGuildMemberList(guildId, nextToken.startIndex, nextToken.roleIndex, count, true, result) + } else { + Result.success(nextToken.copy(finish = true) to result) + } + } else { + Result.success(nextToken to result) + } } suspend fun getSelfGuildInfo(): 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 acf31dc..bf4d669 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 @@ -51,4 +51,28 @@ data class SlowModeInfo( @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 +) + +@Serializable +data class GetGuildMemberListNextToken( + @SerialName("start_index") val startIndex: Long, + @SerialName("role_index") val roleIndex: Long, + @SerialName("seq") val seq: Int, + @SerialName("finish") val finish: Boolean +) + +@Serializable +data class GuildMemberInfo( + @SerialName("tiny_id") val tinyId: Long, + @SerialName("title") val title: String, + @SerialName("nickname") val nickname: String, + @SerialName("role_id") val roleId: Long, + @SerialName("role_name") val roleName: String, + @SerialName("role_color") val roleColor: Long, + @SerialName("join_time") val joinTime: Long, + @SerialName("robot_type") val robotType: Int, + @SerialName("type") val type: Int, + @SerialName("in_black") val inBlack: Boolean, + @SerialName("platform") val platform: Int, +) + diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMemberList.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMemberList.kt index ec59afc..cfe0940 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMemberList.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetGuildMemberList.kt @@ -1,23 +1,77 @@ +@file:OptIn(ExperimentalSerializationApi::class) + package moe.fuqiuluo.shamrock.remote.action.handlers +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.protobuf.ProtoBuf import moe.fuqiuluo.qqinterface.servlet.GProSvc +import moe.fuqiuluo.qqinterface.servlet.structures.GetGuildMemberListNextToken +import moe.fuqiuluo.qqinterface.servlet.structures.GuildMemberInfo +import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.tools.EmptyJsonString +import moe.fuqiuluo.shamrock.tools.hex2ByteArray +import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.symbols.OneBotHandler @OneBotHandler("get_guild_member_list") internal object GetGuildMemberList: IActionHandler() { override suspend fun internalHandle(session: ActionSession): String { val guildId = session.getString("guild_id") - return invoke(guildId.toULong(), session.echo) + val all = session.getBooleanOrDefault("all", false) + return invoke(guildId.toULong(), all, session.getStringOrNull("next_token") ?: "", session.echo) } - suspend operator fun invoke(guildId: ULong, echo: JsonElement = EmptyJsonString): String { - GProSvc.getGuildMemberList(guildId) - return "" + suspend operator fun invoke(guildId: ULong, all: Boolean, nextTokenStr: String, echo: JsonElement = EmptyJsonString): String { + val curNextToken = if (nextTokenStr.isEmpty()) null else ProtoBuf.decodeFromByteArray(nextTokenStr.hex2ByteArray()) + val result = GProSvc.getGuildMemberList( + guildId = guildId, + fetchAll = all, + startIndex = curNextToken?.startIndex ?: 0, + roleIndex = curNextToken?.roleIndex ?: 1, + count = 50 + ) + result.onFailure { + return error(it.message ?: "unable to fetch guild member list", echo) + } + val nextToken = result.getOrThrow().first + val members = arrayListOf() + result.getOrThrow().second.forEach { + it.members.forEach { user -> + members.add(GuildMemberInfo( + tinyId = user.tinyId, + title = user.memberName, + nickname = user.nickName, + roleId = user.roleManagementTag.roleId, + roleName = user.roleManagementTag.tagName, + roleColor = user.roleManagementTag.color, + joinTime = user.joinTime, + robotType = user.robotType, + type = user.type, + inBlack = user.inBlack, + platform = user.platform + )) + } + } + return ok(GetGuildMemberListResult( + members = members, + finish = nextToken.finish, + nextToken = ProtoBuf.encodeToByteArray(nextToken).toHexString(), + )) } override val requiredParams: Array = arrayOf("guild_id") + + @Serializable + data class GetGuildMemberListResult( + @SerialName("members") val members: List, + @SerialName("next_token") val nextToken: String, + @SerialName("finished") val finish: Boolean + ) } \ 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 f56fa90..745a3c1 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 @@ -26,7 +26,8 @@ fun Routing.guildAction() { getOrPost("/get_guild_member_list") { val guildId = fetchOrThrow("guild_id") - call.respondText(GetGuildMemberList(guildId.toULong()), ContentType.Application.Json) + val all = fetchGetOrNull("all")?.toBoolean() ?: false + call.respondText(GetGuildMemberList(guildId.toULong(), all, fetchOrNull("next_token") ?: ""), ContentType.Application.Json) } getOrPost("/get_guild_meta_by_guest") {