Shamrock: support /get_guild_channel_list

This commit is contained in:
白池 2024-02-01 14:56:30 +08:00
parent 103381c17a
commit e629981218
8 changed files with 183 additions and 22 deletions

View File

@ -37,6 +37,7 @@ data class Oidb0xf57Meta(
@ProtoNumber(18) val ownerId: ULong = ULong.MIN_VALUE, @ProtoNumber(18) val ownerId: ULong = ULong.MIN_VALUE,
@ProtoNumber(19) val coverSeq: Long = Long.MIN_VALUE, @ProtoNumber(19) val coverSeq: Long = Long.MIN_VALUE,
@ProtoNumber(20) val clientId: Int = Int.MIN_VALUE, @ProtoNumber(20) val clientId: Int = Int.MIN_VALUE,
@ProtoNumber(27) val displayId: String? = null,
) )
@Serializable @Serializable
@ -72,7 +73,10 @@ data class Oidb0xf57U1(
@ProtoNumber(5003) val u18: UInt = UInt.MIN_VALUE, @ProtoNumber(5003) val u18: UInt = UInt.MIN_VALUE,
@ProtoNumber(5004) val u19: UInt = UInt.MIN_VALUE, @ProtoNumber(5004) val u19: UInt = UInt.MIN_VALUE,
@ProtoNumber(5005) val u20: 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(10007) val u21: UInt = UInt.MIN_VALUE,
@ProtoNumber(15) val u22: UInt = UInt.MIN_VALUE,
@ProtoNumber(30001) val u24: UInt = UInt.MIN_VALUE,
) )
@Serializable @Serializable
@ -85,4 +89,11 @@ data class Oidb0xf57U2(
@ProtoNumber(15) val u6: UInt = UInt.MIN_VALUE, @ProtoNumber(15) val u6: UInt = UInt.MIN_VALUE,
@ProtoNumber(16) val u7: UInt = UInt.MIN_VALUE, @ProtoNumber(16) val u7: UInt = UInt.MIN_VALUE,
@ProtoNumber(17) val u8: 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,
) )

View File

@ -440,7 +440,7 @@ public interface IGPSService extends IRuntimeService {
void getChannelInvisibleRoleList(String str, String str2, @NonNull com.tencent.mobileqq.qqguildsdk.data.type.e eVar);*/ void getChannelInvisibleRoleList(String str, String str2, @NonNull com.tencent.mobileqq.qqguildsdk.data.type.e eVar);*/
@NonNull @NonNull
List<IGProChannelInfo> getChannelList(String str); List<IGProChannelInfo> getChannelList(String str);
/* /*
@Nullable @Nullable

View File

@ -4,14 +4,15 @@ package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.qqguildsdk.api.IGPSService import com.tencent.mobileqq.qqguildsdk.api.IGPSService
import com.tencent.qqnt.kernel.nativeinterface.GProRoleMemberList 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.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf 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.GuildInfo
import moe.fuqiuluo.qqinterface.servlet.structures.GuildStatus 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.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.PlatformUtils 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.cmd0xf88.Oidb0xf88Rsp
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Filter import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Filter
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57GuildInfo 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.Oidb0xf57MetaInfo
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Req import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Req
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Rsp import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57Rsp
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U1 import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U1
import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U2 import moe.whitechi73.protobuf.oidb.cmx0xf57.Oidb0xf57U2
import tencent.im.oidb.cmd0xeac.oidb_0xeac
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
internal object GProSvc: BaseSvc() { internal object GProSvc: BaseSvc() {
@ -40,8 +39,8 @@ internal object GProSvc: BaseSvc() {
suspend fun getGuildInfo(guildId: ULong): Result<Oidb0xf57MetaInfo> { suspend fun getGuildInfo(guildId: ULong): Result<Oidb0xf57MetaInfo> {
val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf57_9", 0xf57, 9, ProtoBuf.encodeToByteArray(Oidb0xf57Req( val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf57_9", 0xf57, 9, ProtoBuf.encodeToByteArray(Oidb0xf57Req(
filter = Oidb0xf57Filter( 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), 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) u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)
), ),
guildInfo = Oidb0xf57GuildInfo(guildId = guildId) guildInfo = Oidb0xf57GuildInfo(guildId = guildId)
))) )))
@ -57,10 +56,50 @@ internal object GProSvc: BaseSvc() {
} }
} }
fun getChannelList(guildId: ULong, refresh: Boolean = false): Result<ArrayList<GProChannelInfo>> {
if (refresh) {
refreshGuildInfo(guildId)
}
val result = arrayListOf<GProChannelInfo>()
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<List<GProRoleMemberList>> { suspend fun getGuildMemberList(guildId: ULong): Result<List<GProRoleMemberList>> {
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
//kernelGProService.fetchMemberListWithRole() //kernelGProService.fetchMemberListWithRole()
return Result.failure(Exception("todo")) return Result.failure(Exception("todo"))
} }
@ -91,18 +130,27 @@ internal object GProSvc: BaseSvc() {
} }
} }
fun getGuildList(refresh: Boolean = false): ArrayList<GuildInfo> { private fun getGuildListByOldApi(result: ArrayList<GuildInfo>) {
PlatformUtils.requireMinQQVersion(version = PlatformUtils.QQ_9_0_8_VER) app.getRuntimeService(IGPSService::class.java, "all").guildList?.forEach {
result.add(GuildInfo(
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService guildId = it.guildID.toLong(),
if (refresh) { guildName = it.guildName ?: "",
kernelGProService.refreshGuildList(true) guildDisplayId = it.guildNumber ?: "",
kernelGProService.guildListFromCache.forEach { profile = it.profile ?: "",
kernelGProService.refreshGuildInfo(it.guildId, true, 1) status = GuildStatus(
} isEnable = !it.isFrozen && !it.isBanned,
isBanned = it.isBanned,
isFrozen = it.isFrozen
),
ownerId = 0,
shutUpTime = it.shutUpExpireTime,
allowSearch = it.allowSearch
))
} }
}
val result = arrayListOf<GuildInfo>() private fun getGuildListByNt(result: ArrayList<GuildInfo>) {
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
kernelGProService.guildListFromCache.forEach { kernelGProService.guildListFromCache.forEach {
if (it.result != 0) return@forEach if (it.result != 0) return@forEach
val guildInfo = it.guildInfo val guildInfo = it.guildInfo
@ -121,6 +169,27 @@ internal object GProSvc: BaseSvc() {
allowSearch = guildInfo.allowSearch == 1 allowSearch = guildInfo.allowSearch == 1
)) ))
} }
}
fun getGuildList(refresh: Boolean = false, forceOldApi: Boolean): ArrayList<GuildInfo> {
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
if (refresh) {
kernelGProService.refreshGuildList(true)
kernelGProService.guildListFromCache.forEach {
refreshGuildInfo(it.guildId.toULong())
}
}
val result = arrayListOf<GuildInfo>()
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 return result
} }

View File

@ -20,4 +20,35 @@ data class GuildStatus(
@SerialName("is_enable") var isEnable: Boolean, @SerialName("is_enable") var isEnable: Boolean,
@SerialName("is_banned") var isBanned: Boolean, @SerialName("is_banned") var isBanned: Boolean,
@SerialName("is_frozen") var isFrozen: 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<SlowModeInfo>,
@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
) )

View File

@ -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<String> = arrayOf("guild_id")
@Serializable
data class GuildChannelListResult(
@SerialName("channel_list") val channelList: List<GProChannelInfo>
)
}

View File

@ -18,11 +18,13 @@ import mqq.app.MobileQQ
@OneBotHandler("get_guild_list") @OneBotHandler("get_guild_list")
internal object GetGuildList : IActionHandler() { internal object GetGuildList : IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String { 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 { operator fun invoke(refresh: Boolean = true, oldSdk: Boolean, echo: JsonElement = EmptyJsonString): String {
val result = GProSvc.getGuildList(refresh) val result = GProSvc.getGuildList(refresh, oldSdk)
return ok(GuildListResult(result), echo, "success") return ok(GuildListResult(result), echo, "success")
} }

View File

@ -3,6 +3,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.qqinterface.servlet.GProSvc import moe.fuqiuluo.qqinterface.servlet.GProSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.remote.action.IActionHandler
@ -35,7 +36,8 @@ internal object GetGuildMetaByGuest: IActionHandler() {
maxRobotCount = meta.robotMaxNum, maxRobotCount = meta.robotMaxNum,
maxAdminCount = meta.adminMaxNum, maxAdminCount = meta.adminMaxNum,
memberCount = meta.memberCount, memberCount = meta.memberCount,
ownerId = meta.ownerId ownerId = meta.ownerId,
guildDisplayId = meta.displayId ?: ""
), echo) ), echo)
} }
@ -52,5 +54,6 @@ internal object GetGuildMetaByGuest: IActionHandler() {
@SerialName("max_admin_count") val maxAdminCount: Int, @SerialName("max_admin_count") val maxAdminCount: Int,
@SerialName("member_count") val memberCount: Long, @SerialName("member_count") val memberCount: Long,
@SerialName("owner_id") val ownerId: ULong, @SerialName("owner_id") val ownerId: ULong,
@SerialName("guild_display_id") val guildDisplayId: String
) )
} }

View File

@ -4,10 +4,13 @@ import io.ktor.http.ContentType
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
import io.ktor.server.routing.Routing 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.GetGuildList
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberList import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberList
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMetaByGuest import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMetaByGuest
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildServiceProfile 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.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.getOrPost import moe.fuqiuluo.shamrock.tools.getOrPost
@ -17,7 +20,8 @@ fun Routing.guildAction() {
} }
getOrPost("/get_guild_list") { 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") { getOrPost("/get_guild_member_list") {
@ -29,4 +33,10 @@ fun Routing.guildAction() {
val guildId = fetchOrThrow("guild_id") val guildId = fetchOrThrow("guild_id")
call.respondText(GetGuildMetaByGuest(guildId.toULong()), ContentType.Application.Json) 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)
}
} }