diff --git a/qqinterface/src/main/java/tencent/im/oidb/cmd0x899/oidb_0x899.java b/qqinterface/src/main/java/tencent/im/oidb/cmd0x899/oidb_0x899.java new file mode 100644 index 0000000..7858698 --- /dev/null +++ b/qqinterface/src/main/java/tencent/im/oidb/cmd0x899/oidb_0x899.java @@ -0,0 +1,84 @@ +package tencent.im.oidb.cmd0x899; + +import com.tencent.mobileqq.pb.ByteStringMicro; +import com.tencent.mobileqq.pb.MessageMicro; +import com.tencent.mobileqq.pb.PBBytesField; +import com.tencent.mobileqq.pb.PBField; +import com.tencent.mobileqq.pb.PBRepeatField; +import com.tencent.mobileqq.pb.PBRepeatMessageField; +import com.tencent.mobileqq.pb.PBUInt32Field; +import com.tencent.mobileqq.pb.PBUInt64Field; + +public class oidb_0x899 { + public static class ReqBody extends MessageMicro { + //static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 42, 48, 56, 64}, new String[]{ + // "uint64_group_code", + // "uint64_start_uin", + // "uint32_identify_flag", + // "rpt_uint64_uin_list", + // "memberlist_opt", + // "uint32_member_num", "uint32_filter_method", "uint32_online_flag"}, new Object[]{0L, 0L, 0, 0L, null, 0, 0, 0}, ReqBody.class); + public final PBUInt64Field uint64_group_code = PBField.initUInt64(0); + public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0); + public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0); + public final PBRepeatField rpt_uint64_uin_list = PBField.initRepeat(PBUInt64Field.__repeatHelper__); + public memberlist memberlist_opt = new memberlist(); + public final PBUInt32Field uint32_member_num = PBField.initUInt32(0); + public final PBUInt32Field uint32_filter_method = PBField.initUInt32(0); + public final PBUInt32Field uint32_online_flag = PBField.initUInt32(0); + } + + public static class RspBody extends MessageMicro { + static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 34, 42}, new String[]{"uint64_group_code", "uint64_start_uin", "uint32_identify_flag", "rpt_memberlist", "str_errorinfo"}, new Object[]{0L, 0L, 0, null, ByteStringMicro.EMPTY}, RspBody.class); + public final PBUInt64Field uint64_group_code = PBField.initUInt64(0); + public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0); + public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0); + public final PBRepeatMessageField rpt_memberlist = PBField.initRepeatMessage(memberlist.class); + public final PBBytesField str_errorinfo = PBField.initBytes(ByteStringMicro.EMPTY); + } + + public static class memberlist extends MessageMicro { + static final MessageMicro.FieldMap __fieldMap__; + public final PBBytesField bytes_rich_info; + public final PBBytesField bytes_special_title; + public final PBBytesField bytes_uin_key; + public final PBUInt32Field uint32_active_day; + public final PBUInt32Field uint32_privilege; + public final PBUInt32Field uint32_special_title_expire_time; + public final PBUInt64Field uint64_member_uin = PBField.initUInt64(0); + public final PBUInt32Field uint32_uin_flag = PBField.initUInt32(0); + public final PBUInt32Field uint32_uin_flagex = PBField.initUInt32(0); + public final PBUInt32Field uint32_uin_mobile_flag = PBField.initUInt32(0); + public final PBUInt32Field uint32_uin_arch_flag = PBField.initUInt32(0); + public final PBUInt32Field uint32_join_time = PBField.initUInt32(0); + public final PBUInt32Field uint32_old_msg_seq = PBField.initUInt32(0); + public final PBUInt32Field uint32_new_msg_seq = PBField.initUInt32(0); + public final PBUInt32Field uint32_last_speak_time = PBField.initUInt32(0); + public final PBUInt32Field uint32_level = PBField.initUInt32(0); + public final PBUInt32Field uint32_point = PBField.initUInt32(0); + public final PBUInt32Field uint32_shutup_timestap = PBField.initUInt32(0); + public final PBUInt32Field uint32_flagex2 = PBField.initUInt32(0); + + static { + ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY; + __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 114, 120, 128, 138, 144, 154}, new String[]{ + "uint64_member_uin", // 1 + + "uint32_uin_flag", "uint32_uin_flagex", "uint32_uin_mobile_flag", "uint32_uin_arch_flag", "uint32_join_time", "uint32_old_msg_seq", "uint32_new_msg_seq", "uint32_last_speak_time", "uint32_level", "uint32_point", + + "uint32_shutup_timestap", //12 + + "uint32_flagex2", "bytes_special_title", "uint32_special_title_expire_time", "uint32_active_day", "bytes_uin_key", "uint32_privilege", "bytes_rich_info"}, new Object[]{0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byteStringMicro, 0, 0, byteStringMicro, 0, byteStringMicro}, memberlist.class); + } + + public memberlist() { + ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY; + this.bytes_special_title = PBField.initBytes(byteStringMicro); + this.uint32_special_title_expire_time = PBField.initUInt32(0); + this.uint32_active_day = PBField.initUInt32(0); + this.bytes_uin_key = PBField.initBytes(byteStringMicro); + this.uint32_privilege = PBField.initUInt32(0); + this.bytes_rich_info = PBField.initBytes(byteStringMicro); + } + } +} diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt index 3d4067e..7fd4a09 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt @@ -1,15 +1,8 @@ package moe.fuqiuluo.qqinterface.servlet import com.tencent.mobileqq.pb.ByteStringMicro -import com.tencent.qqnt.kernel.nativeinterface.DeleteGroupFileResult -import com.tencent.qqnt.kernel.nativeinterface.GroupFileCommonResult -import com.tencent.qqnt.kernel.nativeinterface.IDeleteGroupFileCallback -import io.ktor.util.Deflate -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.withTimeoutOrNull -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable import moe.fuqiuluo.proto.protobufOf +import moe.fuqiuluo.qqinterface.servlet.entries.* import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter @@ -17,11 +10,8 @@ import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.utils.DeflateTools -import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import tencent.im.oidb.cmd0x6d8.oidb_0x6d8 import tencent.im.oidb.oidb_sso -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine internal object FileSvc: BaseSvc() { fun createFileFolder(groupId: String, folderName: String) { @@ -204,49 +194,4 @@ internal object FileSvc: BaseSvc() { LogCenter.log(it.message + ", buffer: ${rspGetFileListBuffer.toHexString()}", Level.ERROR) } } - - @Serializable - data class FileUrl( - @SerialName("url") val url: String, - ) - - @Serializable - data class GroupFileList( - @SerialName("files") val files: List, - @SerialName("folders") val folders: List, - ) - - @Serializable - data class FileInfo( - @SerialName("group_id") val groupId: Long, - @SerialName("file_id") val fileId: String, - @SerialName("file_name") val fileName: String, - @SerialName("file_size") val fileSize: Long, - @SerialName("busid") val busid: Int, - @SerialName("upload_time") val uploadTime: Int, - @SerialName("dead_time") val deadTime: Int, - @SerialName("modify_time") val modifyTime: Int, - @SerialName("download_times") val downloadTimes: Int, - @SerialName("uploader") val uploadUin: Long, - @SerialName("upload_name") val uploadNick: String, - ) - - @Serializable - data class FolderInfo( - @SerialName("group_id") val groupId: Long, - @SerialName("folder_id") val folderId: String, - @SerialName("folder_name") val folderName: String, - @SerialName("total_file_count") val totalFileCount: Int, - @SerialName("create_time") val createTime: Int, - @SerialName("creator") val creator: Long, - @SerialName("creator_name") val creatorNick: String, - ) - - @Serializable - data class FileSystemInfo( - @SerialName("file_count") val fileCount: Int, - @SerialName("limit_count") val fileLimitCount: Int, - @SerialName("used_space") val usedSpace: Long, - @SerialName("total_space") val totalSpace: Long, - ) } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt index 171b7fc..2308515 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt @@ -8,8 +8,6 @@ import com.tencent.mobileqq.app.QQAppInterface import com.tencent.mobileqq.data.troop.TroopInfo import com.tencent.mobileqq.data.troop.TroopMemberInfo import com.tencent.mobileqq.pb.ByteStringMicro -import com.tencent.mobileqq.qroute.QRoute -import com.tencent.mobileqq.relation.api.IAddFriendTempApi import com.tencent.mobileqq.troop.api.ITroopInfoService import com.tencent.mobileqq.troop.api.ITroopMemberInfoService import com.tencent.protofile.join_group_link.join_group_link @@ -26,17 +24,16 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.proto.ProtoUtils import moe.fuqiuluo.proto.asInt -import moe.fuqiuluo.proto.asList -import moe.fuqiuluo.proto.asLong import moe.fuqiuluo.proto.asUtf8String import moe.fuqiuluo.proto.protobufOf +import moe.fuqiuluo.qqinterface.servlet.entries.ProhibitedMemberInfo import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import moe.fuqiuluo.shamrock.tools.putBuf32Long import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher -import mqq.app.MobileQQ +import tencent.im.oidb.cmd0x899.oidb_0x899 import tencent.im.oidb.cmd0x89a.oidb_0x89a import tencent.im.oidb.cmd0x8a0.oidb_0x8a0 import tencent.im.oidb.cmd0x8fc.Oidb_0x8fc @@ -63,6 +60,28 @@ internal object GroupSvc: BaseSvc() { private lateinit var METHOD_REQ_TROOP_MEM_LIST: Method private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method + suspend fun getProhibitedMemberList(groupId: Long): Result> { + val buffer = sendOidbAW("OidbSvc.0x899_0", 2201, 0, oidb_0x899.ReqBody().apply { + uint64_group_code.set(groupId) + uint64_start_uin.set(0) + uint32_identify_flag.set(6) + memberlist_opt.set(oidb_0x899.memberlist().apply { + uint64_member_uin.set(0) + uint32_shutup_timestap.set(0) + }) + }.toByteArray()) ?: return Result.failure(RuntimeException("[oidb] timeout")) + val body = oidb_sso.OIDBSSOPkg() + body.mergeFrom(buffer.slice(4)) + if(body.uint32_result.get() != 0) { + return Result.failure(RuntimeException(body.str_error_msg.get())) + } + + val resp = oidb_0x899.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray()) + return Result.success(resp.rpt_memberlist.get().map { + ProhibitedMemberInfo(it.uint64_member_uin.get(), it.uint32_shutup_timestap.get()) + }) + } + fun poke(groupId: String, userId: String) { sendOidb("OidbSvc.0xed3", 3795, 1, protobufOf( 1 to userId.toLong(), @@ -158,10 +177,8 @@ internal object GroupSvc: BaseSvc() { 3 to rand ).toByteArray() val buffer = sendOidbAW("OidbSvc.0xeac_1", 3756, 1, array) + ?: return Pair(false, "unknown error") val body = oidb_sso.OIDBSSOPkg() - if (buffer == null) { - return Pair(false, "unknown error") - } body.mergeFrom(buffer.slice(4)) val result = ProtoUtils.decodeFromByteArray(body.bytes_bodybuffer.get().toByteArray()) return if (result.has(1)) { diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/Files.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/Files.kt new file mode 100644 index 0000000..43e9253 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/Files.kt @@ -0,0 +1,49 @@ +package moe.fuqiuluo.qqinterface.servlet.entries + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FileUrl( + @SerialName("url") val url: String, +) + +@Serializable +data class GroupFileList( + @SerialName("files") val files: List, + @SerialName("folders") val folders: List, +) + +@Serializable +data class FileInfo( + @SerialName("group_id") val groupId: Long, + @SerialName("file_id") val fileId: String, + @SerialName("file_name") val fileName: String, + @SerialName("file_size") val fileSize: Long, + @SerialName("busid") val busid: Int, + @SerialName("upload_time") val uploadTime: Int, + @SerialName("dead_time") val deadTime: Int, + @SerialName("modify_time") val modifyTime: Int, + @SerialName("download_times") val downloadTimes: Int, + @SerialName("uploader") val uploadUin: Long, + @SerialName("upload_name") val uploadNick: String, +) + +@Serializable +data class FolderInfo( + @SerialName("group_id") val groupId: Long, + @SerialName("folder_id") val folderId: String, + @SerialName("folder_name") val folderName: String, + @SerialName("total_file_count") val totalFileCount: Int, + @SerialName("create_time") val createTime: Int, + @SerialName("creator") val creator: Long, + @SerialName("creator_name") val creatorNick: String, +) + +@Serializable +data class FileSystemInfo( + @SerialName("file_count") val fileCount: Int, + @SerialName("limit_count") val fileLimitCount: Int, + @SerialName("used_space") val usedSpace: Long, + @SerialName("total_space") val totalSpace: Long, +) \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/GroupEntries.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/GroupEntries.kt new file mode 100644 index 0000000..f03fd56 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/entries/GroupEntries.kt @@ -0,0 +1,10 @@ +package moe.fuqiuluo.qqinterface.servlet.entries + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class ProhibitedMemberInfo( + @SerialName("user_id") val memberUin: Long, + @SerialName("time") val shutuptimestap: Int +) \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt index a3259a8..8c5fe88 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt @@ -30,6 +30,7 @@ internal object ActionManager { // GroupActions ModifyTroopName, LeaveTroop, KickTroopMember, BanTroopMember, SetGroupWholeBan, SetGroupAdmin, ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke, SetEssenceMessage, DeleteEssenceMessage, + GetProhibitedMemberList, // MSG ACTIONS SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendGroupForwardMsg, SendGroupMessage, SendPrivateMessage, diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetProhibitedMemberList.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetProhibitedMemberList.kt new file mode 100644 index 0000000..425d9aa --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetProhibitedMemberList.kt @@ -0,0 +1,29 @@ +package moe.fuqiuluo.shamrock.remote.action.handlers + +import kotlinx.serialization.json.JsonElement +import moe.fuqiuluo.qqinterface.servlet.GroupSvc +import moe.fuqiuluo.shamrock.remote.action.ActionSession +import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.tools.EmptyJsonString + +internal object GetProhibitedMemberList: IActionHandler() { + override suspend fun internalHandle(session: ActionSession): String { + val groupCode = session.getLong("group_id") + return invoke(groupCode, session.echo) + } + + suspend operator fun invoke( + groupCode: Long, + echo: JsonElement = EmptyJsonString + ): String { + val result = GroupSvc.getProhibitedMemberList(groupCode) + if (result.isFailure) { + return error(result.exceptionOrNull()?.message ?: "获取禁言列表失败", echo) + } + return ok(result.getOrThrow(), echo) + } + + override val requiredParams: Array = arrayOf("group_id") + + override fun path(): String = "get_prohibited_member_list" +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt index ac2e039..4848c46 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt @@ -13,6 +13,11 @@ import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.getOrPost fun Routing.troopAction() { + getOrPost("/get_prohibited_member_list") { + val groupId = fetchOrThrow("group_id").toLong() + call.respondText(GetProhibitedMemberList(groupId), ContentType.Application.Json) + } + getOrPost("/group_touch") { val groupId = fetchOrThrow("group_id") val userId = fetchOrThrow("user_id") @@ -21,31 +26,36 @@ fun Routing.troopAction() { getOrPost("/get_group_honor_info") { val groupId = fetchOrThrow("group_id") - val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: false + val refresh = fetchOrNull("no_cache")?.toBooleanStrict() + ?: fetchOrNull("refresh")?.toBooleanStrict() ?: false call.respondText(GetTroopHonor(groupId, refresh), ContentType.Application.Json) } getOrPost("/get_group_member_list") { val groupId = fetchOrThrow("group_id") - val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: false + val refresh = fetchOrNull("no_cache")?.toBooleanStrict() + ?: fetchOrNull("refresh")?.toBooleanStrict() ?: false call.respondText(GetTroopMemberList(groupId, refresh), ContentType.Application.Json) } getOrPost("/get_group_member_info") { val groupId = fetchOrThrow("group_id") val userId = fetchOrThrow("user_id") - val refresh = fetchOrNull("no_cache")?.toBooleanStrict() ?: false + val refresh = fetchOrNull("no_cache")?.toBooleanStrict() + ?: fetchOrNull("refresh")?.toBooleanStrict() ?: false call.respondText(GetTroopMemberInfo(groupId, userId, refresh), ContentType.Application.Json) } getOrPost("/get_group_list") { - val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: true + val refresh = fetchOrNull("refresh")?.toBooleanStrict() + ?: fetchOrNull("refresh")?.toBooleanStrict() ?: true call.respondText(GetTroopList(refresh), ContentType.Application.Json) } getOrPost("/get_group_info") { val groupId = fetchOrThrow("group_id") - val refresh = fetchOrNull("no_cache")?.toBooleanStrict() ?: false + val refresh = fetchOrNull("no_cache")?.toBooleanStrict() + ?: fetchOrNull("refresh")?.toBooleanStrict() ?: false call.respondText(GetTroopInfo(groupId, refresh), ContentType.Application.Json) } diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt index 342f2bd..079ed78 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt @@ -1,6 +1,7 @@ @file:OptIn(DelicateCoroutinesApi::class) package moe.fuqiuluo.shamrock.remote.service.listener +import android.os.Build import moe.fuqiuluo.shamrock.helper.MessageHelper import com.tencent.qqnt.kernel.nativeinterface.* import kotlinx.coroutines.DelicateCoroutinesApi @@ -16,6 +17,7 @@ import moe.fuqiuluo.shamrock.helper.db.MessageDB import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter import moe.fuqiuluo.shamrock.remote.service.data.push.MessageTempSource import moe.fuqiuluo.shamrock.remote.service.data.push.PostType +import mqq.app.MobileQQ import java.util.ArrayList import java.util.Collections import kotlin.collections.HashMap