mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
22 Commits
1.0.8
...
63ce2d40bd
Author | SHA1 | Date | |
---|---|---|---|
63ce2d40bd | |||
36f8b6e54b | |||
58413044e9 | |||
3395cd9d95 | |||
494b1f1fd0 | |||
cf943fd13a | |||
9608b46799 | |||
502956e3ec | |||
27b4c26da7 | |||
65f54360f8 | |||
9a9fad975f | |||
7153b21cd4 | |||
fdb2486090 | |||
d60b2a25d1 | |||
2d8dde6951 | |||
78fd60dade | |||
80dbf6af28 | |||
1e53753b5a | |||
e727877268 | |||
63b69df3ea | |||
b03e02675b | |||
e68a1ffd37 |
12
SECURITY.md
12
SECURITY.md
@ -1,11 +1,19 @@
|
||||
# Security Policy
|
||||
|
||||
## Support Version
|
||||
## 支持的版本
|
||||
|
||||
| Version | Supported |
|
||||
| 版本 | 支持状态 |
|
||||
| ------- | ------------------ |
|
||||
| 9.0.15 | :white_check_mark: |
|
||||
| 8.9.75 | :white_check_mark: |
|
||||
| 8.9.73 | :white_check_mark: |
|
||||
| 8.9.98 | :white_check_mark: |
|
||||
| < 8.9.68| :x: |
|
||||
|
||||
## 频道支持性说明
|
||||
|
||||
如果需要使用`频道`相关功能,请升级QQ到9.0.8版本!
|
||||
|
||||
## Riru检测问题
|
||||
|
||||
QQ自`9.0.8`开始将会检测riru,可能作为封号因素。
|
||||
|
@ -229,6 +229,16 @@ object ShamrockConfig {
|
||||
return preferences.getBoolean("anti_qq_trace", true)
|
||||
}
|
||||
|
||||
fun isForbidUselessProcess(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("forbid_useless_process", false)
|
||||
}
|
||||
|
||||
fun setForbidUselessProcess(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("forbid_useless_process", v).apply()
|
||||
}
|
||||
|
||||
fun setAntiTrace(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("anti_qq_trace", v).apply()
|
||||
@ -333,6 +343,7 @@ object ShamrockConfig {
|
||||
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
||||
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", false),
|
||||
"disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false),
|
||||
"forbid_useless_process" to preferences.getBoolean("forbid_useless_process", false)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -100,17 +100,16 @@ fun LabFragment() {
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
/*
|
||||
Function(
|
||||
title = "自动清理QQ垃圾",
|
||||
desc = "也许会导致奇怪的问题(无效)。",
|
||||
title = "禁止无用进程",
|
||||
desc = "禁止QQ生成无用进程浪费内存",
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.isAutoClean(ctx)
|
||||
isSwitch = ShamrockConfig.isForbidUselessProcess(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAutoClean(ctx, it)
|
||||
ShamrockConfig.setForbidUselessProcess(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function false
|
||||
}*/
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "自回复测试",
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.tencent.qqnt.kernel.api.impl;
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IGetTempChatInfoCallback;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo;
|
||||
@ -24,4 +25,8 @@ public class MsgService {
|
||||
public void prepareTempChat(TempChatPrepareInfo tempChatPrepareInfo, IOperateCallback cb) {
|
||||
|
||||
}
|
||||
|
||||
public void getTempChatInfo(int chatType, @Nullable String uid, @Nullable IGetTempChatInfoCallback cb) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public interface IGetTempChatInfoCallback {
|
||||
void onResult(int code, String msg, TempChatInfo info);
|
||||
}
|
@ -59,6 +59,7 @@ import moe.fuqiuluo.shamrock.remote.service.data.EssenceMessage
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessageImage
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.push.MemberRole
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonArray
|
||||
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
||||
import moe.fuqiuluo.shamrock.tools.asInt
|
||||
@ -96,6 +97,8 @@ import java.nio.ByteBuffer
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
internal object GroupSvc: BaseSvc() {
|
||||
private const val GET_MEMBER_ROLE_BY_NT = false
|
||||
|
||||
private val RefreshTroopMemberInfoLock by lazy {
|
||||
Mutex()
|
||||
}
|
||||
@ -394,6 +397,27 @@ internal object GroupSvc: BaseSvc() {
|
||||
.filter { it != 0L }
|
||||
}
|
||||
|
||||
suspend fun getMemberRole(groupId: Long, memberUin: Long): MemberRole {
|
||||
if (!GET_MEMBER_ROLE_BY_NT) {
|
||||
return when (memberUin) {
|
||||
getOwner(groupId.toString()) -> MemberRole.Owner
|
||||
in getAdminList(groupId.toString()) -> MemberRole.Admin
|
||||
else -> MemberRole.Member
|
||||
}
|
||||
}
|
||||
return when(getTroopMemberInfoByUinViaNt(groupId.toString(), memberUin, 3000).getOrNull()?.role) {
|
||||
com.tencent.qqnt.kernel.nativeinterface.MemberRole.STRANGER -> MemberRole.Stranger
|
||||
com.tencent.qqnt.kernel.nativeinterface.MemberRole.MEMBER -> MemberRole.Member
|
||||
com.tencent.qqnt.kernel.nativeinterface.MemberRole.ADMIN -> MemberRole.Admin
|
||||
com.tencent.qqnt.kernel.nativeinterface.MemberRole.OWNER -> MemberRole.Owner
|
||||
com.tencent.qqnt.kernel.nativeinterface.MemberRole.UNSPECIFIED, null -> when (memberUin) {
|
||||
getOwner(groupId.toString()) -> MemberRole.Owner
|
||||
in getAdminList(groupId.toString()) -> MemberRole.Admin
|
||||
else -> MemberRole.Member
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOwner(groupId: String): Long {
|
||||
val groupInfo = getGroupInfo(groupId)
|
||||
return groupInfo.troopowneruin?.toLong() ?: 0
|
||||
@ -566,11 +590,72 @@ internal object GroupSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getTroopMemberInfoByUinViaNt(groupId: String, qq: Long): Result<MemberInfo> {
|
||||
suspend fun getTroopMemberInfoByUinV2(
|
||||
groupId: String,
|
||||
uin: String,
|
||||
refresh: Boolean = false
|
||||
): Result<TroopMemberInfo> {
|
||||
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all")
|
||||
var info = service.getTroopMember(groupId, uin)
|
||||
if (refresh || !service.isMemberInCache(groupId, uin) || info == null || info.troopnick == null) {
|
||||
info = requestTroopMemberInfo(service, groupId.toLong(), uin.toLong(), timeout = 2000).getOrNull()
|
||||
}
|
||||
if (info == null) {
|
||||
info = getTroopMemberInfoByUinViaNt(groupId, uin.toLong(), timeout = 2000L).getOrNull()?.let {
|
||||
TroopMemberInfo().apply {
|
||||
troopnick = it.cardName
|
||||
friendnick = it.nick
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (info != null && (info.alias == null || info.alias.isBlank())) {
|
||||
val req = group_member_info.ReqBody()
|
||||
req.uint64_group_code.set(groupId.toLong())
|
||||
req.uint64_uin.set(uin.toLong())
|
||||
req.bool_new_client.set(true)
|
||||
req.uint32_client_type.set(1)
|
||||
req.uint32_rich_card_name_ver.set(1)
|
||||
val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray(), timeout = 2000)
|
||||
if (respBuffer != null) {
|
||||
val rsp = group_member_info.RspBody()
|
||||
rsp.mergeFrom(respBuffer.slice(4))
|
||||
if (rsp.msg_meminfo.str_location.has()) {
|
||||
info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8()
|
||||
}
|
||||
if (rsp.msg_meminfo.uint32_age.has()) {
|
||||
info.age = rsp.msg_meminfo.uint32_age.get().toByte()
|
||||
}
|
||||
if (rsp.msg_meminfo.bytes_group_honor.has()) {
|
||||
val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray()
|
||||
val honor = troop_honor.GroupUserCardHonor()
|
||||
honor.mergeFrom(honorBytes)
|
||||
info.level = honor.level.get()
|
||||
// 10315: medal_id not real group level
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LogCenter.log(err.stackTraceToString(), Level.WARN)
|
||||
}
|
||||
return if (info != null) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
Result.failure(Exception("获取群成员信息失败"))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTroopMemberInfoByUinViaNt(
|
||||
groupId: String,
|
||||
qq: Long,
|
||||
timeout: Long = 5000L
|
||||
): Result<MemberInfo> {
|
||||
return runCatching {
|
||||
val kernelService = NTServiceFetcher.kernelService
|
||||
val sessionService = kernelService.wrapperSession
|
||||
val groupService = sessionService.groupService
|
||||
val info = suspendCancellableCoroutine {
|
||||
val info = withTimeoutOrNull(timeout) {
|
||||
suspendCancellableCoroutine {
|
||||
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
|
||||
if (code != 0) {
|
||||
it.resume(null)
|
||||
@ -585,12 +670,14 @@ internal object GroupSvc: BaseSvc() {
|
||||
it.resume(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (info != null) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
Result.failure(Exception("获取群成员信息失败"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTroopMemberInfoByUid(groupId: Long, uid: String): Result<MemberInfo> {
|
||||
val kernelService = NTServiceFetcher.kernelService
|
||||
@ -748,7 +835,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestTroopMemberInfo(service: ITroopMemberInfoService, groupId: Long, memberUin: Long): Result<TroopMemberInfo> {
|
||||
private suspend fun requestTroopMemberInfo(service: ITroopMemberInfoService, groupId: Long, memberUin: Long, timeout: Long = 10_000): Result<TroopMemberInfo> {
|
||||
val info = RefreshTroopMemberInfoLock.withLock {
|
||||
val groupIdStr = groupId.toString()
|
||||
val memberUinStr = memberUin.toString()
|
||||
@ -758,7 +845,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
requestMemberInfoV2(groupId, memberUin)
|
||||
requestMemberInfo(groupId, memberUin)
|
||||
|
||||
withTimeoutOrNull(10000) {
|
||||
withTimeoutOrNull(timeout) {
|
||||
while (!service.isMemberInCache(groupIdStr, memberUinStr)) {
|
||||
delay(200)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo
|
||||
import com.tencent.qqnt.msg.api.IMsgService
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
@ -42,9 +43,12 @@ internal object MsgSvc: BaseSvc() {
|
||||
msgService.prepareTempChat(TempChatPrepareInfo(
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
||||
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
||||
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
|
||||
.getTroopMemberNameRemarkFirst(groupId, peerId),
|
||||
groupId, EMPTY_BYTE_ARRAY, app.currentUid, "", TempChatGameSession()
|
||||
app.getRuntimeService(ITroopMemberNameService::class.java, "all").getTroopMemberNameRemarkFirst(groupId, peerId),
|
||||
groupId,
|
||||
EMPTY_BYTE_ARRAY,
|
||||
app.currentUid,
|
||||
"",
|
||||
TempChatGameSession()
|
||||
)) { code, reason ->
|
||||
if (code != 0) {
|
||||
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
||||
@ -53,6 +57,24 @@ internal object MsgSvc: BaseSvc() {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
suspend fun getTempChatInfo(chatType: Int, uid: String): Result<TempChatInfo> {
|
||||
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
|
||||
?: return Result.failure(Exception("获取消息服务失败"))
|
||||
val info: TempChatInfo = withTimeoutOrNull(5000) {
|
||||
suspendCancellableCoroutine {
|
||||
msgService.getTempChatInfo(chatType, uid) { code, msg, tempChatInfo ->
|
||||
if (code == 0) {
|
||||
it.resume(tempChatInfo)
|
||||
} else {
|
||||
LogCenter.log("获取临时会话信息失败: $code:$msg", Level.ERROR)
|
||||
it.resume(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: return Result.failure(Exception("获取临时会话信息失败"))
|
||||
return Result.success(info)
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常获取
|
||||
*/
|
||||
@ -183,8 +205,8 @@ internal object MsgSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
val result = MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
||||
result.onFailure {
|
||||
LogCenter.log("sendToAio: " + it.stackTraceToString(), Level.ERROR)
|
||||
if (result.isFailure) {
|
||||
LogCenter.log("sendToAio: " + result.exceptionOrNull()?.stackTraceToString(), Level.ERROR)
|
||||
return result
|
||||
}
|
||||
val sendResult = result.getOrThrow()
|
||||
|
@ -670,7 +670,9 @@ internal object MessageMaker {
|
||||
at.atNtUid = "0"
|
||||
}
|
||||
else -> {
|
||||
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure {
|
||||
val name = data["name"].asStringOrNull
|
||||
if (name == null) {
|
||||
val info = GroupSvc.getTroopMemberInfoByUinV2(peerId, qq, true).onFailure {
|
||||
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
||||
}.getOrNull()
|
||||
if (info != null) {
|
||||
@ -680,7 +682,10 @@ internal object MessageMaker {
|
||||
.ifNullOrEmpty(qq)
|
||||
}"
|
||||
} else {
|
||||
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}"
|
||||
at.content = "@$qq"
|
||||
}
|
||||
} else {
|
||||
at.content = "@$name"
|
||||
}
|
||||
at.atType = MsgConstant.ATTYPEONE
|
||||
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
||||
|
@ -6,6 +6,7 @@ import com.tencent.mobileqq.transfile.TransferRequest
|
||||
import moe.fuqiuluo.shamrock.utils.MD5
|
||||
import java.io.File
|
||||
import moe.fuqiuluo.qqinterface.servlet.transfile.ResourceType.*
|
||||
import moe.fuqiuluo.shamrock.helper.TransfileHelper
|
||||
|
||||
internal object Transfer: FileTransfer() {
|
||||
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
|
||||
@ -84,11 +85,14 @@ internal object Transfer: FileTransfer() {
|
||||
file: File,
|
||||
wait: Boolean = true
|
||||
): Boolean {
|
||||
return transC2CResource(peerId, file, FileMsg.TRANSFILE_TYPE_PIC, SEND_MSG_BUSINESS_TYPE_PIC_SHARE, wait) {
|
||||
return transC2CResource(peerId, file, FileMsg.TRANSFILE_TYPE_PIC, SEND_MSG_BUSINESS_TYPE_PIC_CAMERA, wait) {
|
||||
val picUpExtraInfo = TransferRequest.PicUpExtraInfo()
|
||||
picUpExtraInfo.mIsRaw = true
|
||||
picUpExtraInfo.mIsRaw = false
|
||||
picUpExtraInfo.mUinType = FileMsg.UIN_BUDDY
|
||||
it.mPicSendSource = 8
|
||||
it.mExtraObj = picUpExtraInfo
|
||||
it.mIsPresend = true
|
||||
it.delayShowProgressTimeInMs = 2000
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,10 +101,13 @@ internal object Transfer: FileTransfer() {
|
||||
file: File,
|
||||
wait: Boolean = true
|
||||
): Boolean {
|
||||
return transTroopResource(groupId, file, FileMsg.TRANSFILE_TYPE_PIC, SEND_MSG_BUSINESS_TYPE_PIC_SHARE, wait) {
|
||||
return transTroopResource(groupId, file, FileMsg.TRANSFILE_TYPE_PIC, SEND_MSG_BUSINESS_TYPE_PIC_CAMERA, wait) {
|
||||
val picUpExtraInfo = TransferRequest.PicUpExtraInfo()
|
||||
picUpExtraInfo.mIsRaw = true
|
||||
//picUpExtraInfo.mIsRaw = !TransfileHelper.isGifFile(file)
|
||||
picUpExtraInfo.mIsRaw = false
|
||||
picUpExtraInfo.mUinType = FileMsg.UIN_TROOP
|
||||
it.mPicSendSource = 8
|
||||
it.delayShowProgressTimeInMs = 2000
|
||||
it.mExtraObj = picUpExtraInfo
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +353,21 @@ internal object MessageHelper {
|
||||
database.messageMappingDao().insert(mapping)
|
||||
}
|
||||
|
||||
fun saveMsgMappingNotExist(
|
||||
hash: Int,
|
||||
qqMsgId: Long,
|
||||
time: Long,
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
subPeerId: String,
|
||||
msgSeq: Int,
|
||||
subChatType: Int = chatType
|
||||
) {
|
||||
val database = MessageDB.getInstance()
|
||||
val mapping = MessageMapping(hash, qqMsgId, chatType, subChatType, peerId, time, msgSeq, subPeerId)
|
||||
database.messageMappingDao().insertNotExist(mapping)
|
||||
}
|
||||
|
||||
external fun createMessageUniseq(chatType: Int, time: Long): Long
|
||||
|
||||
fun decodeCQCode(code: String): JsonArray {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package moe.fuqiuluo.shamrock.helper
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
|
||||
internal object TransfileHelper {
|
||||
private val extensionMap = mapOf(
|
||||
@ -94,4 +96,15 @@ internal object TransfileHelper {
|
||||
val extension = name.substring(index)
|
||||
return extensionMap[extension] ?: -1
|
||||
}
|
||||
|
||||
fun isGifFile(picFile: File): Boolean {
|
||||
if (picFile.exists() && picFile.length() > 3) {
|
||||
return RandomAccessFile(picFile, "r").use {
|
||||
val bArr = ByteArray(3)
|
||||
it.read(bArr)
|
||||
if (bArr[0].toInt() == 71 && bArr[1].toInt() == 73 && bArr[2].toInt() == 70) { return true } else false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
@ -30,6 +30,9 @@ interface MessageMappingDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(mapping: MessageMapping)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
fun insertNotExist(mapping: MessageMapping)
|
||||
|
||||
@Query("UPDATE message_mapping_v2 SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
||||
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@ -18,8 +19,10 @@ internal object GetGProChannelList: IActionHandler() {
|
||||
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)
|
||||
suspend operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
|
||||
val result = withTimeoutOrNull(5000) {
|
||||
GProSvc.getChannelList(guildId, refresh)
|
||||
} ?: return error("timeout", echo)
|
||||
result.onFailure {
|
||||
return error(it.message ?: "unable to fetch channel list", echo)
|
||||
}
|
||||
|
@ -59,6 +59,16 @@ internal object GetHistoryMsg: IActionHandler() {
|
||||
val msgList = ArrayList<MessageDetail>().apply {
|
||||
addAll(result.data!!.map { msg ->
|
||||
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
|
||||
MessageHelper.saveMsgMappingNotExist(
|
||||
hash = msgHash,
|
||||
qqMsgId = msg.msgId,
|
||||
chatType = msg.chatType,
|
||||
subChatType = msg.chatType,
|
||||
peerId = peerId,
|
||||
msgSeq = msg.msgSeq.toInt(),
|
||||
time = msg.msgTime,
|
||||
subPeerId = msg.channelId ?: peerId
|
||||
)
|
||||
MessageDetail(
|
||||
time = msg.msgTime.toInt(),
|
||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||
|
@ -52,11 +52,7 @@ internal object GetTroopMemberInfo : IActionHandler() {
|
||||
area = info.alias ?: "",
|
||||
lastSentTime = info.last_active_time,
|
||||
level = info.level,
|
||||
role = when {
|
||||
GroupSvc.getOwner(groupId).toString() == uin -> MemberRole.Owner
|
||||
uin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
|
||||
else -> MemberRole.Member
|
||||
},
|
||||
role = GroupSvc.getMemberRole(groupId.toLong(), uin.toLong()),
|
||||
unfriendly = false,
|
||||
title = info.mUniqueTitle ?: "",
|
||||
titleExpireTime = info.mUniqueTitleExpire,
|
||||
|
@ -54,12 +54,13 @@ internal object GetTroopMemberList : IActionHandler() {
|
||||
area = info.alias ?: "",
|
||||
lastSentTime = info.last_active_time,
|
||||
level = info.level,
|
||||
role = when {
|
||||
role = GroupSvc.getMemberRole(groupId.toLong(), info.memberuin.toLong())
|
||||
/*when {
|
||||
GroupSvc.getOwner(groupId)
|
||||
.toString() == info.memberuin -> MemberRole.Owner
|
||||
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
|
||||
else -> MemberRole.Member
|
||||
},
|
||||
}*/,
|
||||
unfriendly = false,
|
||||
title = info.mUniqueTitle ?: "",
|
||||
titleExpireTime = info.mUniqueTitleExpire,
|
||||
|
@ -2,6 +2,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
@ -17,7 +18,7 @@ internal object ModifyTroopMemberName: IActionHandler() {
|
||||
}
|
||||
|
||||
operator fun invoke(groupId: String, userId: String, card: String, echo: JsonElement = EmptyJsonString): String {
|
||||
if (!GroupSvc.isAdmin(groupId)) {
|
||||
if (!GroupSvc.isAdmin(groupId) && userId != TicketSvc.getUin()) {
|
||||
return logic("you are not admin", echo)
|
||||
}
|
||||
return if(GroupSvc.modifyGroupMemberCard(groupId.toLong(), userId.toLong(), card))
|
||||
|
@ -5,6 +5,7 @@ import com.tencent.mobileqq.transfile.TransferRequest
|
||||
import com.tencent.mobileqq.transfile.api.ITransFileController
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.post
|
||||
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
||||
import moe.fuqiuluo.shamrock.remote.structures.Status
|
||||
import moe.fuqiuluo.shamrock.tools.fetchPost
|
||||
import moe.fuqiuluo.shamrock.tools.respond
|
||||
@ -15,7 +16,7 @@ import kotlin.random.Random
|
||||
import kotlin.random.nextLong
|
||||
|
||||
fun Routing.registerBDH() {
|
||||
post("/upload_group_image") {
|
||||
if(ShamrockConfig.isDev()) post("/upload_group_image") {
|
||||
val troop = fetchPost("troop")
|
||||
val picBytes = Base64.decode(fetchPost("pic"), Base64.DEFAULT)
|
||||
val md5Str = MD5.getMd5Hex(picBytes)
|
||||
@ -46,5 +47,4 @@ fun Routing.registerBDH() {
|
||||
.transferAsync(transferRequest)
|
||||
respond(isOk = true, Status.Ok, "$md5Str.jpg")
|
||||
}
|
||||
|
||||
}
|
@ -52,7 +52,6 @@ internal object HttpService: HttpTransmitServlet() {
|
||||
GlobalEventTransmitter.onNoticeEvent { event ->
|
||||
pushTo(event)
|
||||
}
|
||||
|
||||
})
|
||||
submitFlowJob(GlobalScope.launch {
|
||||
GlobalEventTransmitter.onRequestEvent {
|
||||
|
@ -1,9 +1,15 @@
|
||||
@file:OptIn(DelicateCoroutinesApi::class)
|
||||
|
||||
package moe.fuqiuluo.shamrock.remote.service.api
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||
@ -48,7 +54,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
|
||||
|
||||
/**
|
||||
* 消息 手淫器
|
||||
* 消息
|
||||
*/
|
||||
object MessageTransmitter {
|
||||
/**
|
||||
@ -86,11 +92,11 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
.ifEmpty { record.sendMemberName }
|
||||
.ifEmpty { record.peerName },
|
||||
card = record.sendMemberName,
|
||||
role = when (record.senderUin) {
|
||||
role = GroupSvc.getMemberRole(record.peerUin, record.senderUin)/*when (record.senderUin) {
|
||||
GroupSvc.getOwner(record.peerUin.toString()) -> MemberRole.Owner
|
||||
in GroupSvc.getAdminList(record.peerUin.toString()) -> MemberRole.Admin
|
||||
else -> MemberRole.Member
|
||||
},
|
||||
}*/,
|
||||
title = "",
|
||||
level = "",
|
||||
)
|
||||
@ -108,7 +114,9 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
rawMsg: String,
|
||||
msgHash: Int,
|
||||
postType: PostType,
|
||||
tempSource: MessageTempSource = MessageTempSource.Unknown
|
||||
tempSource: MessageTempSource = MessageTempSource.Unknown,
|
||||
groupId: Long = Long.MIN_VALUE,
|
||||
fromNick: String? = null
|
||||
): Boolean {
|
||||
val botUin = app.longAccountUin
|
||||
var nickName = record.sendNickName
|
||||
@ -142,7 +150,9 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
title = "",
|
||||
level = "",
|
||||
),
|
||||
tmpSource = tempSource.id
|
||||
tmpSource = tempSource.id,
|
||||
groupId = groupId,
|
||||
fromNickName = fromNick
|
||||
)
|
||||
)
|
||||
return true
|
||||
@ -172,6 +182,8 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
postType = postType,
|
||||
messageType = MsgType.Guild,
|
||||
subType = MsgSubType.Channel,
|
||||
guildId = record.guildId,
|
||||
channelId = record.channelId,
|
||||
messageId = msgHash,
|
||||
targetId = record.peerUin,
|
||||
peerId = botUin,
|
||||
@ -186,7 +198,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
userId = record.senderUid.toLong(),
|
||||
nickname = nickName,
|
||||
card = record.sendMemberName,
|
||||
role = MemberRole.Member,
|
||||
role = MemberRole.Member, // TODO(GUILD ROLE)
|
||||
title = record.sendNickName,
|
||||
level = record.roleId.toString(),
|
||||
tinyId = record.senderUid
|
||||
@ -554,19 +566,29 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
||||
|
||||
@ShamrockDsl
|
||||
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
|
||||
messageEventFlow.collect(collector)
|
||||
messageEventFlow.collect {
|
||||
GlobalScope.launch {
|
||||
collector.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ShamrockDsl
|
||||
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
|
||||
noticeEventFlow
|
||||
.collect(collector)
|
||||
noticeEventFlow.collect {
|
||||
GlobalScope.launch {
|
||||
collector.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ShamrockDsl
|
||||
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) {
|
||||
requestEventFlow
|
||||
.collect(collector)
|
||||
requestEventFlow.collect {
|
||||
GlobalScope.launch {
|
||||
collector.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,10 @@ internal abstract class WebSocketTransmitServlet(
|
||||
private val sendLock = Mutex()
|
||||
protected val eventReceivers: MutableList<WebSocket> = Collections.synchronizedList(mutableListOf<WebSocket>())
|
||||
|
||||
init {
|
||||
connectionLostTimeout = 0
|
||||
}
|
||||
|
||||
override val address: String
|
||||
get() = "-"
|
||||
|
||||
|
@ -76,6 +76,7 @@ internal object ShamrockConfig {
|
||||
putBoolean("enable_self_msg", intent.getBooleanExtra("enable_self_msg", false)) // 推送自己发的消息
|
||||
putBoolean("shell", intent.getBooleanExtra("shell", false)) // 开启Shell接口
|
||||
putBoolean("enable_sync_msg_as_sent_msg", intent.getBooleanExtra("enable_sync_msg_as_sent_msg", false)) // 推送同步消息
|
||||
putBoolean("forbid_useless_process", intent.getBooleanExtra("forbid_useless_process", false)) // 禁用QQ生成无用进程
|
||||
}
|
||||
Config.defaultToken = intent.getStringExtra("token")
|
||||
Config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
|
||||
@ -126,6 +127,10 @@ internal object ShamrockConfig {
|
||||
return mmkv.getBoolean("enable_self_msg", false)
|
||||
}
|
||||
|
||||
fun forbidUselessProcess(): Boolean {
|
||||
return mmkv.getBoolean("forbid_useless_process", false)
|
||||
}
|
||||
|
||||
fun openWebSocketClient(): Boolean {
|
||||
return mmkv.getBoolean("ws_client", false)
|
||||
}
|
||||
|
@ -52,8 +52,10 @@ internal data class MessageEvent (
|
||||
@SerialName("message_type") val messageType: MsgType,
|
||||
@SerialName("sub_type") val subType: MsgSubType,
|
||||
@SerialName("message_id") val messageId: Int,
|
||||
@SerialName("group_id") val groupId: Long = 0,
|
||||
@SerialName("target_id") val targetId: Long = 0,
|
||||
@SerialName("group_id") val groupId: Long = Long.MIN_VALUE,
|
||||
@SerialName("guild_id") val guildId: String? = null,
|
||||
@SerialName("channel_id") val channelId: String? = null,
|
||||
@SerialName("target_id") val targetId: Long = Long.MIN_VALUE,
|
||||
@SerialName("peer_id") val peerId: Long,
|
||||
@SerialName("user_id") val userId: Long,
|
||||
@SerialName("anonymous") val anonymous: Anonymous? = null,
|
||||
@ -61,7 +63,8 @@ internal data class MessageEvent (
|
||||
@SerialName("raw_message") val rawMessage: String,
|
||||
@SerialName("font") val font: Int,
|
||||
@SerialName("sender") val sender: Sender,
|
||||
@SerialName("temp_source") val tmpSource: Int = -1
|
||||
@SerialName("temp_source") val tmpSource: Int = Int.MIN_VALUE,
|
||||
@SerialName("from_nick") val fromNickName: String? = null
|
||||
)
|
||||
|
||||
enum class MessageTempSource(val id: Int) {
|
||||
@ -86,7 +89,9 @@ internal data class Anonymous(
|
||||
internal enum class MemberRole {
|
||||
@SerialName("owner") Owner,
|
||||
@SerialName("admin") Admin,
|
||||
@SerialName("member") Member
|
||||
@SerialName("member") Member,
|
||||
@SerialName("stranger") Stranger,
|
||||
@SerialName("unknown") Unknown
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -7,6 +7,7 @@ import com.tencent.qqnt.kernel.nativeinterface.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toCQCode
|
||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||
@ -89,10 +90,12 @@ internal object AioListener : IKernelMsgListener {
|
||||
|
||||
if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(
|
||||
record, record.elements, rawMsg, msgHash, postType
|
||||
)) {
|
||||
)
|
||||
) {
|
||||
LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN)
|
||||
}
|
||||
}
|
||||
|
||||
MsgConstant.KCHATTYPEC2C -> {
|
||||
LogCenter.log("私聊消息(private = ${record.senderUin}, id = [$msgHash | ${record.msgId} | ${record.msgSeq}], msg = $rawMsg)")
|
||||
ShamrockConfig.getPrivateRule()?.let { rule ->
|
||||
@ -102,7 +105,8 @@ internal object AioListener : IKernelMsgListener {
|
||||
|
||||
if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
||||
record, record.elements, rawMsg, msgHash, postType
|
||||
)) {
|
||||
)
|
||||
) {
|
||||
LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||
}
|
||||
}
|
||||
@ -110,15 +114,31 @@ internal object AioListener : IKernelMsgListener {
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
||||
if (!ShamrockConfig.allowTempSession()) return
|
||||
|
||||
LogCenter.log("私聊临时消息(private = ${record.senderUin}, id = $msgHash, msg = $rawMsg)")
|
||||
ShamrockConfig.getPrivateRule()?.let { rule ->
|
||||
if (!rule.black.isNullOrEmpty() && rule.black.contains(record.senderUin)) return
|
||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||
}
|
||||
|
||||
var groupCode = 0L
|
||||
var fromNick = ""
|
||||
MsgSvc.getTempChatInfo(record.chatType, record.senderUid).onSuccess {
|
||||
groupCode = it.groupCode.toLong()
|
||||
fromNick = it.fromNick
|
||||
}
|
||||
|
||||
LogCenter.log("私聊临时消息(private = ${record.senderUin}, groupId=$groupCode, id = $msgHash, msg = $rawMsg)")
|
||||
|
||||
if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
||||
record, record.elements, rawMsg, msgHash, tempSource = MessageTempSource.Group, postType = postType
|
||||
)) {
|
||||
record,
|
||||
record.elements,
|
||||
rawMsg,
|
||||
msgHash,
|
||||
tempSource = MessageTempSource.Group,
|
||||
postType = postType,
|
||||
groupId = groupCode,
|
||||
fromNick = fromNick
|
||||
)
|
||||
) {
|
||||
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||
}
|
||||
}
|
||||
@ -174,8 +194,6 @@ internal object AioListener : IKernelMsgListener {
|
||||
|
||||
override fun onMsgInfoListUpdate(msgList: ArrayList<MsgRecord>?) {
|
||||
msgList?.forEach { record ->
|
||||
if (record.chatType == MsgConstant.KCHATTYPEGUILD) return@forEach// TODO: 频道消息暂不处理
|
||||
|
||||
if (record.sendStatus == MsgConstant.KSENDSTATUSFAILED
|
||||
|| record.sendStatus == MsgConstant.KSENDSTATUSSENDING
|
||||
) {
|
||||
@ -449,6 +467,7 @@ internal object AioListener : IKernelMsgListener {
|
||||
override fun onGuildMsgAbFlagChanged(guildMsgAbFlag: GuildMsgAbFlag?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: GuildNotificationAbstractInfo?) {
|
||||
|
||||
}
|
||||
|
@ -534,12 +534,11 @@ internal object PrimitiveListener {
|
||||
val groupCode = event.groupCode
|
||||
val applierUid = event.applierUid
|
||||
val reason = event.applyMsg ?: ""
|
||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||
if (applier == getLongUin()) {
|
||||
return
|
||||
}
|
||||
val msgSeq = contentHead.msgSeq
|
||||
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
||||
val flag = try {
|
||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||
@ -548,10 +547,14 @@ internal object PrimitiveListener {
|
||||
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
|
||||
}
|
||||
val seq = req?.msg_seq?.get() ?: time
|
||||
if (applier == 0L) {
|
||||
applier = req?.req_uin?.get() ?: 0L
|
||||
}
|
||||
"$seq;$groupCode;$applier"
|
||||
} catch (err: Throwable) {
|
||||
"$time;$groupCode;$applier"
|
||||
}
|
||||
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
||||
if (!GlobalEventTransmitter.RequestTransmitter
|
||||
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, RequestSubType.Add)
|
||||
) {
|
||||
@ -562,7 +565,7 @@ internal object PrimitiveListener {
|
||||
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
|
||||
val groupCode = event.applyInfo?.groupCode ?: return
|
||||
val applierUid = event.applyInfo?.applierUid ?: return
|
||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||
if (applier == getLongUin()) {
|
||||
return
|
||||
}
|
||||
@ -570,7 +573,6 @@ internal object PrimitiveListener {
|
||||
// todo
|
||||
return
|
||||
}
|
||||
LogCenter.log("邀请入群申请($groupCode): $applier")
|
||||
val flag = try {
|
||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||
@ -579,10 +581,14 @@ internal object PrimitiveListener {
|
||||
it.msg_time.get() == time
|
||||
}
|
||||
val seq = req?.msg_seq?.get() ?: time
|
||||
if (applier == 0L) {
|
||||
applier = req?.req_uin?.get() ?: 0L
|
||||
}
|
||||
"$seq;$groupCode;$applier"
|
||||
} catch (err: Throwable) {
|
||||
"$time;$groupCode;$applier"
|
||||
}
|
||||
LogCenter.log("邀请入群申请($groupCode): $applier")
|
||||
if (!GlobalEventTransmitter.RequestTransmitter
|
||||
.transGroupApply(time, applier, applierUid, "", groupCode, flag, RequestSubType.Add)
|
||||
) {
|
||||
|
@ -1,10 +1,14 @@
|
||||
package moe.fuqiuluo.shamrock.xposed
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Process
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import de.robv.android.xposed.XposedBridge.log
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
||||
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
|
||||
import moe.fuqiuluo.shamrock.xposed.loader.KeepAlive
|
||||
import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader
|
||||
@ -15,12 +19,14 @@ import moe.fuqiuluo.shamrock.xposed.hooks.runFirstActions
|
||||
import mqq.app.MobileQQ
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
internal const val PACKAGE_NAME_QQ = "com.tencent.mobileqq"
|
||||
internal const val PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi"
|
||||
internal const val PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite"
|
||||
internal const val PACKAGE_NAME_TIM = "com.tencent.tim"
|
||||
private const val PACKAGE_NAME_QQ = "com.tencent.mobileqq"
|
||||
private const val PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi"
|
||||
private const val PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite"
|
||||
private const val PACKAGE_NAME_TIM = "com.tencent.tim"
|
||||
|
||||
private val uselessProcess = listOf("peak", "tool", "mini", "qzone")
|
||||
|
||||
internal class XposedEntry: IXposedHookLoadPackage {
|
||||
companion object {
|
||||
@ -121,9 +127,7 @@ internal class XposedEntry: IXposedHookLoadPackage {
|
||||
System.setProperty("qxbot_flag", "1")
|
||||
} else return
|
||||
|
||||
log("Process Name = " + MobileQQ.getMobileQQ().qqProcessName)
|
||||
|
||||
PlatformUtils.isTim()
|
||||
val processName = MobileQQ.getMobileQQ().qqProcessName
|
||||
|
||||
// MSG LISTENER 进程运行在主进程
|
||||
// API 也应该开放在主进程
|
||||
@ -134,6 +138,22 @@ internal class XposedEntry: IXposedHookLoadPackage {
|
||||
MMKVFetcher.initMMKV(ctx)
|
||||
}
|
||||
|
||||
runCatching {
|
||||
if (ShamrockConfig.forbidUselessProcess()) {
|
||||
if(uselessProcess.any {
|
||||
processName.contains(it, ignoreCase = true)
|
||||
}) {
|
||||
log("[Shamrock] Useless process detected: $processName, exit.")
|
||||
Process.killProcess(Process.myPid())
|
||||
exitProcess(0)
|
||||
}
|
||||
} else {
|
||||
log("[Shamrock] Useless process detection is disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
log("Process Name = $processName")
|
||||
|
||||
runFirstActions(ctx)
|
||||
}
|
||||
}
|
||||
|
@ -2,24 +2,17 @@ package moe.fuqiuluo.shamrock.xposed.helper
|
||||
|
||||
import com.tencent.qqnt.kernel.api.IKernelService
|
||||
import com.tencent.qqnt.kernel.api.impl.MsgService
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelGroupService
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelGuildService
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IQQNTWrapperSession
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.remote.service.PacketReceiver
|
||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
||||
import moe.fuqiuluo.shamrock.remote.service.listener.GroupEventListener
|
||||
import moe.fuqiuluo.shamrock.remote.service.listener.KernelGuildListener
|
||||
import moe.fuqiuluo.shamrock.remote.service.listener.PrimitiveListener
|
||||
import moe.fuqiuluo.shamrock.tools.hookMethod
|
||||
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
|
||||
internal object NTServiceFetcher {
|
||||
private lateinit var iKernelService: IKernelService
|
||||
@ -30,7 +23,7 @@ internal object NTServiceFetcher {
|
||||
lock.withLock {
|
||||
val msgService = service.msgService ?: return
|
||||
val sessionService = service.wrapperSession ?: return
|
||||
val groupService = sessionService.groupService ?: return
|
||||
//val groupService = sessionService.groupService ?: return
|
||||
|
||||
val curHash = service.hashCode() + msgService.hashCode()
|
||||
if (isInitForNt(curHash)) return
|
||||
@ -43,7 +36,7 @@ internal object NTServiceFetcher {
|
||||
this.iKernelService = service
|
||||
|
||||
|
||||
initNTKernelListener(msgService, groupService)
|
||||
initNTKernelListener(msgService)
|
||||
antiBackgroundMode(sessionService)
|
||||
//hookGuildListener(sessionService)
|
||||
}
|
||||
@ -66,7 +59,7 @@ internal object NTServiceFetcher {
|
||||
return hash == curKernelHash
|
||||
}
|
||||
|
||||
private fun initNTKernelListener(msgService: MsgService, groupService: IKernelGroupService) {
|
||||
private fun initNTKernelListener(msgService: MsgService) {
|
||||
if (!PlatformUtils.isMainProcess()) return
|
||||
|
||||
try {
|
||||
|
Reference in New Issue
Block a user