mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
23 Commits
1.0.8
...
3b210d7ed0
Author | SHA1 | Date | |
---|---|---|---|
3b210d7ed0 | |||
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
|
# Security Policy
|
||||||
|
|
||||||
## Support Version
|
## 支持的版本
|
||||||
|
|
||||||
| Version | Supported |
|
| 版本 | 支持状态 |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
|
| 9.0.15 | :white_check_mark: |
|
||||||
| 8.9.75 | :white_check_mark: |
|
| 8.9.75 | :white_check_mark: |
|
||||||
| 8.9.73 | :white_check_mark: |
|
| 8.9.73 | :white_check_mark: |
|
||||||
| 8.9.98 | :white_check_mark: |
|
| 8.9.98 | :white_check_mark: |
|
||||||
| < 8.9.68| :x: |
|
| < 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)
|
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) {
|
fun setAntiTrace(ctx: Context, v: Boolean) {
|
||||||
val preferences = ctx.getSharedPreferences("config", 0)
|
val preferences = ctx.getSharedPreferences("config", 0)
|
||||||
preferences.edit().putBoolean("anti_qq_trace", v).apply()
|
preferences.edit().putBoolean("anti_qq_trace", v).apply()
|
||||||
@ -333,6 +343,7 @@ object ShamrockConfig {
|
|||||||
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
||||||
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", 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),
|
"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
|
thickness = 0.2.dp
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Function(
|
Function(
|
||||||
title = "自动清理QQ垃圾",
|
title = "禁止无用进程",
|
||||||
desc = "也许会导致奇怪的问题(无效)。",
|
desc = "禁止QQ生成无用进程浪费内存",
|
||||||
descColor = color,
|
descColor = color,
|
||||||
isSwitch = ShamrockConfig.isAutoClean(ctx)
|
isSwitch = ShamrockConfig.isForbidUselessProcess(ctx)
|
||||||
) {
|
) {
|
||||||
ShamrockConfig.setAutoClean(ctx, it)
|
ShamrockConfig.setForbidUselessProcess(ctx, it)
|
||||||
ShamrockConfig.pushUpdate(ctx)
|
ShamrockConfig.pushUpdate(ctx)
|
||||||
return@Function false
|
return@Function true
|
||||||
}*/
|
}
|
||||||
|
|
||||||
Function(
|
Function(
|
||||||
title = "自回复测试",
|
title = "自回复测试",
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.tencent.mobileqq.perf.block;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import epic.EIPCClient;
|
||||||
|
import epic.EIPCResult;
|
||||||
|
import kotlin.Metadata;
|
||||||
|
import kotlin.jvm.JvmStatic;
|
||||||
|
|
||||||
|
public final class BinderMethodProxy {
|
||||||
|
@NotNull
|
||||||
|
public static final BinderMethodProxy INSTANCE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
INSTANCE = new BinderMethodProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
public static EIPCResult callServer(@NotNull EIPCClient client, @Nullable String module, @Nullable String action, @Nullable Bundle bundle) {
|
||||||
|
//MainBlockMethodMonitor.onMethodStart();
|
||||||
|
//EIPCResult callServer = client.callServer(str, str2, bundle);
|
||||||
|
//MainBlockMethodMonitor.onMethodEnd();
|
||||||
|
//return callServer;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.tencent.mobileqq.qipc;
|
||||||
|
|
||||||
|
import epic.EIPCClient;
|
||||||
|
|
||||||
|
public class QIPCClientHelper {
|
||||||
|
public static synchronized QIPCClientHelper getInstance() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EIPCClient getClient() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.tencent.mobileqq.qmmkv;
|
||||||
|
|
||||||
|
public class MMKVOptionEntity {
|
||||||
|
public String decodeString(String str, String str2) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.tencent.mobileqq.qmmkv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public class QMMKV {
|
||||||
|
public static MMKVOptionEntity from(Context context, String str) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.tencent.mobileqq.transfile.dns;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class IpData implements Parcelable {
|
||||||
|
public int mFailedCount;
|
||||||
|
public String mIp;
|
||||||
|
public int mPort;
|
||||||
|
public int mType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe the kinds of special objects contained in this Parcelable
|
||||||
|
* instance's marshaled representation. For example, if the object will
|
||||||
|
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
|
||||||
|
* the return value of this method must include the
|
||||||
|
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
|
||||||
|
*
|
||||||
|
* @return a bitmask indicating the set of special object types marshaled
|
||||||
|
* by this Parcelable object instance.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten this object in to a Parcel.
|
||||||
|
*
|
||||||
|
* @param dest The Parcel in which the object should be written.
|
||||||
|
* @param flags Additional flags about how the object should be written.
|
||||||
|
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.tencent.qqnt.kernel.api.impl;
|
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.IKernelMsgListener;
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
|
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo;
|
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo;
|
||||||
@ -24,4 +25,8 @@ public class MsgService {
|
|||||||
public void prepareTempChat(TempChatPrepareInfo tempChatPrepareInfo, IOperateCallback cb) {
|
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);
|
||||||
|
}
|
4
qqinterface/src/main/java/epic/EIPCClient.java
Normal file
4
qqinterface/src/main/java/epic/EIPCClient.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package epic;
|
||||||
|
|
||||||
|
public class EIPCClient {
|
||||||
|
}
|
11
qqinterface/src/main/java/epic/EIPCResult.java
Normal file
11
qqinterface/src/main/java/epic/EIPCResult.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package epic;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class EIPCResult {
|
||||||
|
public Bundle data;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -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.GroupAnnouncement
|
||||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
|
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
|
||||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessageImage
|
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.EmptyJsonArray
|
||||||
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
||||||
import moe.fuqiuluo.shamrock.tools.asInt
|
import moe.fuqiuluo.shamrock.tools.asInt
|
||||||
@ -96,6 +97,8 @@ import java.nio.ByteBuffer
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
internal object GroupSvc: BaseSvc() {
|
internal object GroupSvc: BaseSvc() {
|
||||||
|
private const val GET_MEMBER_ROLE_BY_NT = false
|
||||||
|
|
||||||
private val RefreshTroopMemberInfoLock by lazy {
|
private val RefreshTroopMemberInfoLock by lazy {
|
||||||
Mutex()
|
Mutex()
|
||||||
}
|
}
|
||||||
@ -394,6 +397,27 @@ internal object GroupSvc: BaseSvc() {
|
|||||||
.filter { it != 0L }
|
.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 {
|
fun getOwner(groupId: String): Long {
|
||||||
val groupInfo = getGroupInfo(groupId)
|
val groupInfo = getGroupInfo(groupId)
|
||||||
return groupInfo.troopowneruin?.toLong() ?: 0
|
return groupInfo.troopowneruin?.toLong() ?: 0
|
||||||
@ -566,24 +590,53 @@ internal object GroupSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getTroopMemberInfoByUinViaNt(groupId: String, qq: Long): Result<MemberInfo> {
|
suspend fun getTroopMemberInfoByUinV2(
|
||||||
val kernelService = NTServiceFetcher.kernelService
|
groupId: String,
|
||||||
val sessionService = kernelService.wrapperSession
|
uin: String,
|
||||||
val groupService = sessionService.groupService
|
refresh: Boolean = false
|
||||||
val info = suspendCancellableCoroutine {
|
): Result<TroopMemberInfo> {
|
||||||
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
|
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all")
|
||||||
if (code != 0) {
|
var info = service.getTroopMember(groupId, uin)
|
||||||
it.resume(null)
|
if (refresh || !service.isMemberInCache(groupId, uin) || info == null || info.troopnick == null) {
|
||||||
return@getTransferableMemberInfo
|
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
|
||||||
}
|
}
|
||||||
data.forEach { (_, info) ->
|
}
|
||||||
if (info.uin == qq) {
|
}
|
||||||
it.resume(info)
|
try {
|
||||||
return@forEach
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.resume(null)
|
|
||||||
}
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LogCenter.log(err.stackTraceToString(), Level.WARN)
|
||||||
}
|
}
|
||||||
return if (info != null) {
|
return if (info != null) {
|
||||||
Result.success(info)
|
Result.success(info)
|
||||||
@ -592,6 +645,40 @@ internal object GroupSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = withTimeoutOrNull(timeout) {
|
||||||
|
suspendCancellableCoroutine {
|
||||||
|
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
|
||||||
|
if (code != 0) {
|
||||||
|
it.resume(null)
|
||||||
|
return@getTransferableMemberInfo
|
||||||
|
}
|
||||||
|
data.forEach { (_, info) ->
|
||||||
|
if (info.uin == qq) {
|
||||||
|
it.resume(info)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.resume(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (info != null) {
|
||||||
|
Result.success(info)
|
||||||
|
} else {
|
||||||
|
Result.failure(Exception("获取群成员信息失败"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTroopMemberInfoByUid(groupId: Long, uid: String): Result<MemberInfo> {
|
suspend fun getTroopMemberInfoByUid(groupId: Long, uid: String): Result<MemberInfo> {
|
||||||
val kernelService = NTServiceFetcher.kernelService
|
val kernelService = NTServiceFetcher.kernelService
|
||||||
val sessionService = kernelService.wrapperSession
|
val sessionService = kernelService.wrapperSession
|
||||||
@ -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 info = RefreshTroopMemberInfoLock.withLock {
|
||||||
val groupIdStr = groupId.toString()
|
val groupIdStr = groupId.toString()
|
||||||
val memberUinStr = memberUin.toString()
|
val memberUinStr = memberUin.toString()
|
||||||
@ -758,7 +845,7 @@ internal object GroupSvc: BaseSvc() {
|
|||||||
requestMemberInfoV2(groupId, memberUin)
|
requestMemberInfoV2(groupId, memberUin)
|
||||||
requestMemberInfo(groupId, memberUin)
|
requestMemberInfo(groupId, memberUin)
|
||||||
|
|
||||||
withTimeoutOrNull(10000) {
|
withTimeoutOrNull(timeout) {
|
||||||
while (!service.isMemberInCache(groupIdStr, memberUinStr)) {
|
while (!service.isMemberInCache(groupIdStr, memberUinStr)) {
|
||||||
delay(200)
|
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.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
|
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.kernel.nativeinterface.TempChatPrepareInfo
|
||||||
import com.tencent.qqnt.msg.api.IMsgService
|
import com.tencent.qqnt.msg.api.IMsgService
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
@ -42,9 +43,12 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
msgService.prepareTempChat(TempChatPrepareInfo(
|
msgService.prepareTempChat(TempChatPrepareInfo(
|
||||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
||||||
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
||||||
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
|
app.getRuntimeService(ITroopMemberNameService::class.java, "all").getTroopMemberNameRemarkFirst(groupId, peerId),
|
||||||
.getTroopMemberNameRemarkFirst(groupId, peerId),
|
groupId,
|
||||||
groupId, EMPTY_BYTE_ARRAY, app.currentUid, "", TempChatGameSession()
|
EMPTY_BYTE_ARRAY,
|
||||||
|
app.currentUid,
|
||||||
|
"",
|
||||||
|
TempChatGameSession()
|
||||||
)) { code, reason ->
|
)) { code, reason ->
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
||||||
@ -53,6 +57,24 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
return Result.success(Unit)
|
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))
|
val result = MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
||||||
result.onFailure {
|
if (result.isFailure) {
|
||||||
LogCenter.log("sendToAio: " + it.stackTraceToString(), Level.ERROR)
|
LogCenter.log("sendToAio: " + result.exceptionOrNull()?.stackTraceToString(), Level.ERROR)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
val sendResult = result.getOrThrow()
|
val sendResult = result.getOrThrow()
|
||||||
|
@ -670,17 +670,22 @@ internal object MessageMaker {
|
|||||||
at.atNtUid = "0"
|
at.atNtUid = "0"
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure {
|
val name = data["name"].asStringOrNull
|
||||||
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
if (name == null) {
|
||||||
}.getOrNull()
|
val info = GroupSvc.getTroopMemberInfoByUinV2(peerId, qq, true).onFailure {
|
||||||
if (info != null) {
|
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
||||||
at.content = "@${
|
}.getOrNull()
|
||||||
info.troopnick
|
if (info != null) {
|
||||||
.ifNullOrEmpty(info.friendnick)
|
at.content = "@${
|
||||||
.ifNullOrEmpty(qq)
|
info.troopnick
|
||||||
}"
|
.ifNullOrEmpty(info.friendnick)
|
||||||
|
.ifNullOrEmpty(qq)
|
||||||
|
}"
|
||||||
|
} else {
|
||||||
|
at.content = "@$qq"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}"
|
at.content = "@$name"
|
||||||
}
|
}
|
||||||
at.atType = MsgConstant.ATTYPEONE
|
at.atType = MsgConstant.ATTYPEONE
|
||||||
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.msg.convert
|
package moe.fuqiuluo.qqinterface.servlet.msg.convert
|
||||||
|
|
||||||
|
import com.tencent.mobileqq.qmmkv.QMMKV
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||||
@ -13,6 +14,8 @@ import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
|||||||
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
||||||
import moe.fuqiuluo.shamrock.tools.asString
|
import moe.fuqiuluo.shamrock.tools.asString
|
||||||
import moe.fuqiuluo.shamrock.tools.json
|
import moe.fuqiuluo.shamrock.tools.json
|
||||||
|
import mqq.app.MobileQQ
|
||||||
|
import kotlin.jvm.internal.Intrinsics
|
||||||
|
|
||||||
internal sealed class MessageElemConverter: IMessageConvert {
|
internal sealed class MessageElemConverter: IMessageConvert {
|
||||||
/**
|
/**
|
||||||
@ -135,14 +138,16 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
ImageMapping(md5.uppercase(), chatType, image.fileSize)
|
ImageMapping(md5.uppercase(), chatType, image.fileSize)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//LogCenter.log(image.toString())
|
||||||
|
|
||||||
return MessageSegment(
|
return MessageSegment(
|
||||||
type = "image",
|
type = "image",
|
||||||
data = hashMapOf(
|
data = hashMapOf(
|
||||||
"file" to md5,
|
"file" to md5,
|
||||||
"url" to when(chatType) {
|
"url" to when(chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(md5)
|
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(image.originImageUrl, md5)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(md5)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(image.originImageUrl, md5)
|
||||||
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(md5)
|
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(image.originImageUrl, md5)
|
||||||
else -> unknownChatType(chatType)
|
else -> unknownChatType(chatType)
|
||||||
},
|
},
|
||||||
"subType" to image.picSubType,
|
"subType" to image.picSubType,
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
@file:OptIn(ExperimentalSerializationApi::class)
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import com.tencent.mobileqq.pb.ByteStringMicro
|
import com.tencent.mobileqq.pb.ByteStringMicro
|
||||||
|
import com.tencent.mobileqq.perf.block.BinderMethodProxy
|
||||||
|
import com.tencent.mobileqq.qipc.QIPCClientHelper
|
||||||
import com.tencent.mobileqq.transfile.FileMsg
|
import com.tencent.mobileqq.transfile.FileMsg
|
||||||
import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
||||||
|
import com.tencent.mobileqq.transfile.dns.IpData
|
||||||
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
||||||
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@ -19,18 +23,35 @@ import moe.fuqiuluo.shamrock.tools.slice
|
|||||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||||
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||||
|
import mqq.app.MobileQQ
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody
|
||||||
import mqq.app.MobileQQ
|
|
||||||
import tencent.im.cs.cmd0x346.cmd0x346
|
import tencent.im.cs.cmd0x346.cmd0x346
|
||||||
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
|
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
|
||||||
import tencent.im.oidb.cmd0xe37.cmd0xe37
|
import tencent.im.oidb.cmd0xe37.cmd0xe37
|
||||||
import tencent.im.oidb.oidb_sso
|
import tencent.im.oidb.oidb_sso
|
||||||
|
import java.util.ArrayList
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
private const val GPRO_PIC = "gchat.qpic.cn"
|
||||||
|
private const val GPRO_PIC_NT = "multimedia.nt.qq.com.cn"
|
||||||
|
private const val C2C_PIC = "c2cpicdw.qpic.cn"
|
||||||
|
|
||||||
internal object RichProtoSvc: BaseSvc() {
|
internal object RichProtoSvc: BaseSvc() {
|
||||||
|
/*@Deprecated("Use RichProtoSvc.getQQDns instead", ReplaceWith("getQQDns(domain)"))
|
||||||
|
fun getQQDns(domain: String) {
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putString("domain", "xxx")
|
||||||
|
bundle.putInt("businessType", 1)
|
||||||
|
val result = BinderMethodProxy
|
||||||
|
.callServer(QIPCClientHelper.getInstance().client, "InnerDnsModule", "reqDomain2IpList", bundle)
|
||||||
|
if (result.isSuccess) {
|
||||||
|
val ipList: ArrayList<IpData> = result.data.getParcelableArrayList("ip")!!
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
|
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
|
||||||
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(
|
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(
|
||||||
Oidb0xfc2ReqBody(
|
Oidb0xfc2ReqBody(
|
||||||
@ -142,19 +163,35 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getGroupPicDownUrl(
|
fun getGroupPicDownUrl(
|
||||||
|
originalUrl: String,
|
||||||
md5: String
|
md5: String
|
||||||
): String {
|
): String {
|
||||||
return "http://gchat.qpic.cn/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2"
|
val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC
|
||||||
|
if (originalUrl.isNotEmpty()) {
|
||||||
|
return "https://$domain$originalUrl"
|
||||||
|
}
|
||||||
|
return "https://$domain/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getC2CPicDownUrl(
|
fun getC2CPicDownUrl(
|
||||||
|
originalUrl: String,
|
||||||
md5: String
|
md5: String
|
||||||
): String {
|
): String {
|
||||||
return "https://c2cpicdw.qpic.cn/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
|
if (originalUrl.isNotEmpty()) {
|
||||||
|
return "https://$C2C_PIC$originalUrl"
|
||||||
|
}
|
||||||
|
return "https://$C2C_PIC/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGuildPicDownUrl(md5: String): String {
|
fun getGuildPicDownUrl(
|
||||||
return "https://gchat.qpic.cn/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
|
originalUrl: String,
|
||||||
|
md5: String
|
||||||
|
): String {
|
||||||
|
val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC
|
||||||
|
if (originalUrl.isNotEmpty()) {
|
||||||
|
return "https://$domain$originalUrl"
|
||||||
|
}
|
||||||
|
return "https://$domain/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getC2CVideoDownUrl(
|
suspend fun getC2CVideoDownUrl(
|
||||||
@ -321,12 +358,4 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
RichProtoProc.procRichProtoReq(richProtoReq)
|
RichProtoProc.procRichProtoReq(richProtoReq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getGuildPttDownUrl(
|
|
||||||
peerId: String,
|
|
||||||
md5Hex: String,
|
|
||||||
fileUUId: String
|
|
||||||
): String {
|
|
||||||
return "unsupported"
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ import com.tencent.mobileqq.transfile.TransferRequest
|
|||||||
import moe.fuqiuluo.shamrock.utils.MD5
|
import moe.fuqiuluo.shamrock.utils.MD5
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.ResourceType.*
|
import moe.fuqiuluo.qqinterface.servlet.transfile.ResourceType.*
|
||||||
|
import moe.fuqiuluo.shamrock.helper.TransfileHelper
|
||||||
|
|
||||||
internal object Transfer: FileTransfer() {
|
internal object Transfer: FileTransfer() {
|
||||||
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
|
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
|
||||||
@ -84,11 +85,14 @@ internal object Transfer: FileTransfer() {
|
|||||||
file: File,
|
file: File,
|
||||||
wait: Boolean = true
|
wait: Boolean = true
|
||||||
): Boolean {
|
): 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()
|
val picUpExtraInfo = TransferRequest.PicUpExtraInfo()
|
||||||
picUpExtraInfo.mIsRaw = true
|
picUpExtraInfo.mIsRaw = false
|
||||||
|
picUpExtraInfo.mUinType = FileMsg.UIN_BUDDY
|
||||||
it.mPicSendSource = 8
|
it.mPicSendSource = 8
|
||||||
it.mExtraObj = picUpExtraInfo
|
it.mExtraObj = picUpExtraInfo
|
||||||
|
it.mIsPresend = true
|
||||||
|
it.delayShowProgressTimeInMs = 2000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,10 +101,13 @@ internal object Transfer: FileTransfer() {
|
|||||||
file: File,
|
file: File,
|
||||||
wait: Boolean = true
|
wait: Boolean = true
|
||||||
): Boolean {
|
): 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()
|
val picUpExtraInfo = TransferRequest.PicUpExtraInfo()
|
||||||
picUpExtraInfo.mIsRaw = true
|
//picUpExtraInfo.mIsRaw = !TransfileHelper.isGifFile(file)
|
||||||
|
picUpExtraInfo.mIsRaw = false
|
||||||
|
picUpExtraInfo.mUinType = FileMsg.UIN_TROOP
|
||||||
it.mPicSendSource = 8
|
it.mPicSendSource = 8
|
||||||
|
it.delayShowProgressTimeInMs = 2000
|
||||||
it.mExtraObj = picUpExtraInfo
|
it.mExtraObj = picUpExtraInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,6 +353,21 @@ internal object MessageHelper {
|
|||||||
database.messageMappingDao().insert(mapping)
|
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
|
external fun createMessageUniseq(chatType: Int, time: Long): Long
|
||||||
|
|
||||||
fun decodeCQCode(code: String): JsonArray {
|
fun decodeCQCode(code: String): JsonArray {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package moe.fuqiuluo.shamrock.helper
|
package moe.fuqiuluo.shamrock.helper
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
internal object TransfileHelper {
|
internal object TransfileHelper {
|
||||||
private val extensionMap = mapOf(
|
private val extensionMap = mapOf(
|
||||||
@ -94,4 +96,15 @@ internal object TransfileHelper {
|
|||||||
val extension = name.substring(index)
|
val extension = name.substring(index)
|
||||||
return extensionMap[extension] ?: -1
|
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)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(mapping: MessageMapping)
|
fun insert(mapping: MessageMapping)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
|
fun insertNotExist(mapping: MessageMapping)
|
||||||
|
|
||||||
@Query("UPDATE message_mapping_v2 SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
@Query("UPDATE message_mapping_v2 SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
||||||
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
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
|
||||||
@ -18,8 +19,10 @@ internal object GetGProChannelList: IActionHandler() {
|
|||||||
return invoke(guildId.toULong(), refresh, echo = session.echo)
|
return invoke(guildId.toULong(), refresh, echo = session.echo)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
|
suspend operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
|
||||||
val result = GProSvc.getChannelList(guildId, refresh)
|
val result = withTimeoutOrNull(5000) {
|
||||||
|
GProSvc.getChannelList(guildId, refresh)
|
||||||
|
} ?: return error("timeout", echo)
|
||||||
result.onFailure {
|
result.onFailure {
|
||||||
return error(it.message ?: "unable to fetch channel list", echo)
|
return error(it.message ?: "unable to fetch channel list", echo)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,16 @@ internal object GetHistoryMsg: IActionHandler() {
|
|||||||
val msgList = ArrayList<MessageDetail>().apply {
|
val msgList = ArrayList<MessageDetail>().apply {
|
||||||
addAll(result.data!!.map { msg ->
|
addAll(result.data!!.map { msg ->
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
|
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(
|
MessageDetail(
|
||||||
time = msg.msgTime.toInt(),
|
time = msg.msgTime.toInt(),
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||||
|
@ -35,8 +35,8 @@ internal object GetImage: IActionHandler() {
|
|||||||
image.size,
|
image.size,
|
||||||
image.fileName,
|
image.fileName,
|
||||||
when(image.chatType) {
|
when(image.chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(fileMd5)
|
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl("", fileMd5)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(fileMd5)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl("", fileMd5)
|
||||||
else -> error("Not supported chat type: ${image.chatType}, convertMsgElementsToMsgSegment::Pic")
|
else -> error("Not supported chat type: ${image.chatType}, convertMsgElementsToMsgSegment::Pic")
|
||||||
}
|
}
|
||||||
), echo = echo)
|
), echo = echo)
|
||||||
|
@ -52,11 +52,7 @@ internal object GetTroopMemberInfo : IActionHandler() {
|
|||||||
area = info.alias ?: "",
|
area = info.alias ?: "",
|
||||||
lastSentTime = info.last_active_time,
|
lastSentTime = info.last_active_time,
|
||||||
level = info.level,
|
level = info.level,
|
||||||
role = when {
|
role = GroupSvc.getMemberRole(groupId.toLong(), uin.toLong()),
|
||||||
GroupSvc.getOwner(groupId).toString() == uin -> MemberRole.Owner
|
|
||||||
uin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
|
|
||||||
else -> MemberRole.Member
|
|
||||||
},
|
|
||||||
unfriendly = false,
|
unfriendly = false,
|
||||||
title = info.mUniqueTitle ?: "",
|
title = info.mUniqueTitle ?: "",
|
||||||
titleExpireTime = info.mUniqueTitleExpire,
|
titleExpireTime = info.mUniqueTitleExpire,
|
||||||
|
@ -54,12 +54,13 @@ internal object GetTroopMemberList : IActionHandler() {
|
|||||||
area = info.alias ?: "",
|
area = info.alias ?: "",
|
||||||
lastSentTime = info.last_active_time,
|
lastSentTime = info.last_active_time,
|
||||||
level = info.level,
|
level = info.level,
|
||||||
role = when {
|
role = GroupSvc.getMemberRole(groupId.toLong(), info.memberuin.toLong())
|
||||||
|
/*when {
|
||||||
GroupSvc.getOwner(groupId)
|
GroupSvc.getOwner(groupId)
|
||||||
.toString() == info.memberuin -> MemberRole.Owner
|
.toString() == info.memberuin -> MemberRole.Owner
|
||||||
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
|
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
|
||||||
else -> MemberRole.Member
|
else -> MemberRole.Member
|
||||||
},
|
}*/,
|
||||||
unfriendly = false,
|
unfriendly = false,
|
||||||
title = info.mUniqueTitle ?: "",
|
title = info.mUniqueTitle ?: "",
|
||||||
titleExpireTime = info.mUniqueTitleExpire,
|
titleExpireTime = info.mUniqueTitleExpire,
|
||||||
|
@ -2,6 +2,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
|
|||||||
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
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.ActionSession
|
||||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
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 {
|
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 logic("you are not admin", echo)
|
||||||
}
|
}
|
||||||
return if(GroupSvc.modifyGroupMemberCard(groupId.toLong(), userId.toLong(), card))
|
return if(GroupSvc.modifyGroupMemberCard(groupId.toLong(), userId.toLong(), card))
|
||||||
|
@ -103,7 +103,7 @@ internal object UploadGroupFile : IActionHandler() {
|
|||||||
|
|
||||||
// 根据文件大小调整超时时间
|
// 根据文件大小调整超时时间
|
||||||
val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP)
|
val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP)
|
||||||
val info = (withTimeoutOrNull((srcFile.length() / (300 * 1024)) * 1000 + 5000) {
|
val info = (withTimeoutOrNull((srcFile.length() / (125 * 1024)) * 1000 + 5000) {
|
||||||
val msgService = QRoute.api(IMsgService::class.java)
|
val msgService = QRoute.api(IMsgService::class.java)
|
||||||
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
|
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
|
||||||
suspendCancellableCoroutine<FileTransNotifyInfo?> {
|
suspendCancellableCoroutine<FileTransNotifyInfo?> {
|
||||||
|
@ -5,6 +5,7 @@ import com.tencent.mobileqq.transfile.TransferRequest
|
|||||||
import com.tencent.mobileqq.transfile.api.ITransFileController
|
import com.tencent.mobileqq.transfile.api.ITransFileController
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.routing.post
|
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.remote.structures.Status
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPost
|
import moe.fuqiuluo.shamrock.tools.fetchPost
|
||||||
import moe.fuqiuluo.shamrock.tools.respond
|
import moe.fuqiuluo.shamrock.tools.respond
|
||||||
@ -15,7 +16,7 @@ import kotlin.random.Random
|
|||||||
import kotlin.random.nextLong
|
import kotlin.random.nextLong
|
||||||
|
|
||||||
fun Routing.registerBDH() {
|
fun Routing.registerBDH() {
|
||||||
post("/upload_group_image") {
|
if(ShamrockConfig.isDev()) post("/upload_group_image") {
|
||||||
val troop = fetchPost("troop")
|
val troop = fetchPost("troop")
|
||||||
val picBytes = Base64.decode(fetchPost("pic"), Base64.DEFAULT)
|
val picBytes = Base64.decode(fetchPost("pic"), Base64.DEFAULT)
|
||||||
val md5Str = MD5.getMd5Hex(picBytes)
|
val md5Str = MD5.getMd5Hex(picBytes)
|
||||||
@ -46,5 +47,4 @@ fun Routing.registerBDH() {
|
|||||||
.transferAsync(transferRequest)
|
.transferAsync(transferRequest)
|
||||||
respond(isOk = true, Status.Ok, "$md5Str.jpg")
|
respond(isOk = true, Status.Ok, "$md5Str.jpg")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -52,7 +52,6 @@ internal object HttpService: HttpTransmitServlet() {
|
|||||||
GlobalEventTransmitter.onNoticeEvent { event ->
|
GlobalEventTransmitter.onNoticeEvent { event ->
|
||||||
pushTo(event)
|
pushTo(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
submitFlowJob(GlobalScope.launch {
|
submitFlowJob(GlobalScope.launch {
|
||||||
GlobalEventTransmitter.onRequestEvent {
|
GlobalEventTransmitter.onRequestEvent {
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
@file:OptIn(DelicateCoroutinesApi::class)
|
||||||
|
|
||||||
package moe.fuqiuluo.shamrock.remote.service.api
|
package moe.fuqiuluo.shamrock.remote.service.api
|
||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
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.FlowCollector
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
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)
|
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息 手淫器
|
* 消息
|
||||||
*/
|
*/
|
||||||
object MessageTransmitter {
|
object MessageTransmitter {
|
||||||
/**
|
/**
|
||||||
@ -86,11 +92,11 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
.ifEmpty { record.sendMemberName }
|
.ifEmpty { record.sendMemberName }
|
||||||
.ifEmpty { record.peerName },
|
.ifEmpty { record.peerName },
|
||||||
card = record.sendMemberName,
|
card = record.sendMemberName,
|
||||||
role = when (record.senderUin) {
|
role = GroupSvc.getMemberRole(record.peerUin, record.senderUin)/*when (record.senderUin) {
|
||||||
GroupSvc.getOwner(record.peerUin.toString()) -> MemberRole.Owner
|
GroupSvc.getOwner(record.peerUin.toString()) -> MemberRole.Owner
|
||||||
in GroupSvc.getAdminList(record.peerUin.toString()) -> MemberRole.Admin
|
in GroupSvc.getAdminList(record.peerUin.toString()) -> MemberRole.Admin
|
||||||
else -> MemberRole.Member
|
else -> MemberRole.Member
|
||||||
},
|
}*/,
|
||||||
title = "",
|
title = "",
|
||||||
level = "",
|
level = "",
|
||||||
)
|
)
|
||||||
@ -108,7 +114,9 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
rawMsg: String,
|
rawMsg: String,
|
||||||
msgHash: Int,
|
msgHash: Int,
|
||||||
postType: PostType,
|
postType: PostType,
|
||||||
tempSource: MessageTempSource = MessageTempSource.Unknown
|
tempSource: MessageTempSource = MessageTempSource.Unknown,
|
||||||
|
groupId: Long = Long.MIN_VALUE,
|
||||||
|
fromNick: String? = null
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val botUin = app.longAccountUin
|
val botUin = app.longAccountUin
|
||||||
var nickName = record.sendNickName
|
var nickName = record.sendNickName
|
||||||
@ -142,7 +150,9 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
title = "",
|
title = "",
|
||||||
level = "",
|
level = "",
|
||||||
),
|
),
|
||||||
tmpSource = tempSource.id
|
tmpSource = tempSource.id,
|
||||||
|
groupId = groupId,
|
||||||
|
fromNickName = fromNick
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
@ -172,6 +182,8 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
postType = postType,
|
postType = postType,
|
||||||
messageType = MsgType.Guild,
|
messageType = MsgType.Guild,
|
||||||
subType = MsgSubType.Channel,
|
subType = MsgSubType.Channel,
|
||||||
|
guildId = record.guildId,
|
||||||
|
channelId = record.channelId,
|
||||||
messageId = msgHash,
|
messageId = msgHash,
|
||||||
targetId = record.peerUin,
|
targetId = record.peerUin,
|
||||||
peerId = botUin,
|
peerId = botUin,
|
||||||
@ -186,7 +198,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
userId = record.senderUid.toLong(),
|
userId = record.senderUid.toLong(),
|
||||||
nickname = nickName,
|
nickname = nickName,
|
||||||
card = record.sendMemberName,
|
card = record.sendMemberName,
|
||||||
role = MemberRole.Member,
|
role = MemberRole.Member, // TODO(GUILD ROLE)
|
||||||
title = record.sendNickName,
|
title = record.sendNickName,
|
||||||
level = record.roleId.toString(),
|
level = record.roleId.toString(),
|
||||||
tinyId = record.senderUid
|
tinyId = record.senderUid
|
||||||
@ -554,19 +566,29 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
|
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
|
||||||
messageEventFlow.collect(collector)
|
messageEventFlow.collect {
|
||||||
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
|
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
|
||||||
noticeEventFlow
|
noticeEventFlow.collect {
|
||||||
.collect(collector)
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) {
|
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) {
|
||||||
requestEventFlow
|
requestEventFlow.collect {
|
||||||
.collect(collector)
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ internal abstract class WebSocketTransmitServlet(
|
|||||||
private val sendLock = Mutex()
|
private val sendLock = Mutex()
|
||||||
protected val eventReceivers: MutableList<WebSocket> = Collections.synchronizedList(mutableListOf<WebSocket>())
|
protected val eventReceivers: MutableList<WebSocket> = Collections.synchronizedList(mutableListOf<WebSocket>())
|
||||||
|
|
||||||
|
init {
|
||||||
|
connectionLostTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
override val address: String
|
override val address: String
|
||||||
get() = "-"
|
get() = "-"
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ internal object ShamrockConfig {
|
|||||||
putBoolean("enable_self_msg", intent.getBooleanExtra("enable_self_msg", false)) // 推送自己发的消息
|
putBoolean("enable_self_msg", intent.getBooleanExtra("enable_self_msg", false)) // 推送自己发的消息
|
||||||
putBoolean("shell", intent.getBooleanExtra("shell", false)) // 开启Shell接口
|
putBoolean("shell", intent.getBooleanExtra("shell", false)) // 开启Shell接口
|
||||||
putBoolean("enable_sync_msg_as_sent_msg", intent.getBooleanExtra("enable_sync_msg_as_sent_msg", false)) // 推送同步消息
|
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.defaultToken = intent.getStringExtra("token")
|
||||||
Config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
|
Config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
|
||||||
@ -126,6 +127,10 @@ internal object ShamrockConfig {
|
|||||||
return mmkv.getBoolean("enable_self_msg", false)
|
return mmkv.getBoolean("enable_self_msg", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun forbidUselessProcess(): Boolean {
|
||||||
|
return mmkv.getBoolean("forbid_useless_process", false)
|
||||||
|
}
|
||||||
|
|
||||||
fun openWebSocketClient(): Boolean {
|
fun openWebSocketClient(): Boolean {
|
||||||
return mmkv.getBoolean("ws_client", false)
|
return mmkv.getBoolean("ws_client", false)
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,10 @@ internal data class MessageEvent (
|
|||||||
@SerialName("message_type") val messageType: MsgType,
|
@SerialName("message_type") val messageType: MsgType,
|
||||||
@SerialName("sub_type") val subType: MsgSubType,
|
@SerialName("sub_type") val subType: MsgSubType,
|
||||||
@SerialName("message_id") val messageId: Int,
|
@SerialName("message_id") val messageId: Int,
|
||||||
@SerialName("group_id") val groupId: Long = 0,
|
@SerialName("group_id") val groupId: Long = Long.MIN_VALUE,
|
||||||
@SerialName("target_id") val targetId: Long = 0,
|
@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("peer_id") val peerId: Long,
|
||||||
@SerialName("user_id") val userId: Long,
|
@SerialName("user_id") val userId: Long,
|
||||||
@SerialName("anonymous") val anonymous: Anonymous? = null,
|
@SerialName("anonymous") val anonymous: Anonymous? = null,
|
||||||
@ -61,7 +63,8 @@ internal data class MessageEvent (
|
|||||||
@SerialName("raw_message") val rawMessage: String,
|
@SerialName("raw_message") val rawMessage: String,
|
||||||
@SerialName("font") val font: Int,
|
@SerialName("font") val font: Int,
|
||||||
@SerialName("sender") val sender: Sender,
|
@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) {
|
enum class MessageTempSource(val id: Int) {
|
||||||
@ -86,7 +89,9 @@ internal data class Anonymous(
|
|||||||
internal enum class MemberRole {
|
internal enum class MemberRole {
|
||||||
@SerialName("owner") Owner,
|
@SerialName("owner") Owner,
|
||||||
@SerialName("admin") Admin,
|
@SerialName("admin") Admin,
|
||||||
@SerialName("member") Member
|
@SerialName("member") Member,
|
||||||
|
@SerialName("stranger") Stranger,
|
||||||
|
@SerialName("unknown") Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -7,6 +7,7 @@ import com.tencent.qqnt.kernel.nativeinterface.*
|
|||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toCQCode
|
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toCQCode
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||||
@ -44,11 +45,11 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
it.value(record)
|
it.value(record)
|
||||||
messageLessListenerMap.remove(it.key)
|
messageLessListenerMap.remove(it.key)
|
||||||
}
|
}
|
||||||
if (record.msgSeq < 0) return
|
if (record.msgSeq < 0) return
|
||||||
|
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -87,12 +88,14 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(
|
if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(
|
||||||
record, record.elements, rawMsg, msgHash, postType
|
record, record.elements, rawMsg, msgHash, postType
|
||||||
)) {
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN)
|
LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> {
|
MsgConstant.KCHATTYPEC2C -> {
|
||||||
LogCenter.log("私聊消息(private = ${record.senderUin}, id = [$msgHash | ${record.msgId} | ${record.msgSeq}], msg = $rawMsg)")
|
LogCenter.log("私聊消息(private = ${record.senderUin}, id = [$msgHash | ${record.msgId} | ${record.msgSeq}], msg = $rawMsg)")
|
||||||
ShamrockConfig.getPrivateRule()?.let { rule ->
|
ShamrockConfig.getPrivateRule()?.let { rule ->
|
||||||
@ -100,9 +103,10 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
||||||
record, record.elements, rawMsg, msgHash, postType
|
record, record.elements, rawMsg, msgHash, postType
|
||||||
)) {
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,24 +114,40 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
||||||
if (!ShamrockConfig.allowTempSession()) return
|
if (!ShamrockConfig.allowTempSession()) return
|
||||||
|
|
||||||
LogCenter.log("私聊临时消息(private = ${record.senderUin}, id = $msgHash, msg = $rawMsg)")
|
|
||||||
ShamrockConfig.getPrivateRule()?.let { rule ->
|
ShamrockConfig.getPrivateRule()?.let { rule ->
|
||||||
if (!rule.black.isNullOrEmpty() && rule.black.contains(record.senderUin)) return
|
if (!rule.black.isNullOrEmpty() && rule.black.contains(record.senderUin)) return
|
||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
var groupCode = 0L
|
||||||
record, record.elements, rawMsg, msgHash, tempSource = MessageTempSource.Group, postType = postType
|
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,
|
||||||
|
groupId = groupCode,
|
||||||
|
fromNick = fromNick
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEGUILD -> {
|
MsgConstant.KCHATTYPEGUILD -> {
|
||||||
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter
|
if (!GlobalEventTransmitter.MessageTransmitter
|
||||||
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
||||||
) {
|
) {
|
||||||
LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +170,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
try {
|
try {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -174,8 +194,6 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
override fun onMsgInfoListUpdate(msgList: ArrayList<MsgRecord>?) {
|
override fun onMsgInfoListUpdate(msgList: ArrayList<MsgRecord>?) {
|
||||||
msgList?.forEach { record ->
|
msgList?.forEach { record ->
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEGUILD) return@forEach// TODO: 频道消息暂不处理
|
|
||||||
|
|
||||||
if (record.sendStatus == MsgConstant.KSENDSTATUSFAILED
|
if (record.sendStatus == MsgConstant.KSENDSTATUSFAILED
|
||||||
|| record.sendStatus == MsgConstant.KSENDSTATUSSENDING
|
|| record.sendStatus == MsgConstant.KSENDSTATUSSENDING
|
||||||
) {
|
) {
|
||||||
@ -184,7 +202,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -449,6 +467,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
override fun onGuildMsgAbFlagChanged(guildMsgAbFlag: GuildMsgAbFlag?) {
|
override fun onGuildMsgAbFlagChanged(guildMsgAbFlag: GuildMsgAbFlag?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: GuildNotificationAbstractInfo?) {
|
override fun onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: GuildNotificationAbstractInfo?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -534,12 +534,11 @@ internal object PrimitiveListener {
|
|||||||
val groupCode = event.groupCode
|
val groupCode = event.groupCode
|
||||||
val applierUid = event.applierUid
|
val applierUid = event.applierUid
|
||||||
val reason = event.applyMsg ?: ""
|
val reason = event.applyMsg ?: ""
|
||||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||||
if (applier == getLongUin()) {
|
if (applier == getLongUin()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val msgSeq = contentHead.msgSeq
|
val msgSeq = contentHead.msgSeq
|
||||||
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
|
||||||
val flag = try {
|
val flag = try {
|
||||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||||
@ -548,10 +547,14 @@ internal object PrimitiveListener {
|
|||||||
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
|
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
|
||||||
}
|
}
|
||||||
val seq = req?.msg_seq?.get() ?: time
|
val seq = req?.msg_seq?.get() ?: time
|
||||||
|
if (applier == 0L) {
|
||||||
|
applier = req?.req_uin?.get() ?: 0L
|
||||||
|
}
|
||||||
"$seq;$groupCode;$applier"
|
"$seq;$groupCode;$applier"
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
"$time;$groupCode;$applier"
|
"$time;$groupCode;$applier"
|
||||||
}
|
}
|
||||||
|
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
||||||
if (!GlobalEventTransmitter.RequestTransmitter
|
if (!GlobalEventTransmitter.RequestTransmitter
|
||||||
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, RequestSubType.Add)
|
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, RequestSubType.Add)
|
||||||
) {
|
) {
|
||||||
@ -562,7 +565,7 @@ internal object PrimitiveListener {
|
|||||||
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
|
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
|
||||||
val groupCode = event.applyInfo?.groupCode ?: return
|
val groupCode = event.applyInfo?.groupCode ?: return
|
||||||
val applierUid = event.applyInfo?.applierUid ?: return
|
val applierUid = event.applyInfo?.applierUid ?: return
|
||||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||||
if (applier == getLongUin()) {
|
if (applier == getLongUin()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -570,7 +573,6 @@ internal object PrimitiveListener {
|
|||||||
// todo
|
// todo
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
LogCenter.log("邀请入群申请($groupCode): $applier")
|
|
||||||
val flag = try {
|
val flag = try {
|
||||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||||
@ -579,10 +581,14 @@ internal object PrimitiveListener {
|
|||||||
it.msg_time.get() == time
|
it.msg_time.get() == time
|
||||||
}
|
}
|
||||||
val seq = req?.msg_seq?.get() ?: time
|
val seq = req?.msg_seq?.get() ?: time
|
||||||
|
if (applier == 0L) {
|
||||||
|
applier = req?.req_uin?.get() ?: 0L
|
||||||
|
}
|
||||||
"$seq;$groupCode;$applier"
|
"$seq;$groupCode;$applier"
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
"$time;$groupCode;$applier"
|
"$time;$groupCode;$applier"
|
||||||
}
|
}
|
||||||
|
LogCenter.log("邀请入群申请($groupCode): $applier")
|
||||||
if (!GlobalEventTransmitter.RequestTransmitter
|
if (!GlobalEventTransmitter.RequestTransmitter
|
||||||
.transGroupApply(time, applier, applierUid, "", groupCode, flag, RequestSubType.Add)
|
.transGroupApply(time, applier, applierUid, "", groupCode, flag, RequestSubType.Add)
|
||||||
) {
|
) {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package moe.fuqiuluo.shamrock.xposed
|
package moe.fuqiuluo.shamrock.xposed
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Process
|
||||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import de.robv.android.xposed.XposedBridge.log
|
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.utils.MMKVFetcher
|
||||||
import moe.fuqiuluo.shamrock.xposed.loader.KeepAlive
|
import moe.fuqiuluo.shamrock.xposed.loader.KeepAlive
|
||||||
import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader
|
import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader
|
||||||
@ -15,12 +19,14 @@ import moe.fuqiuluo.shamrock.xposed.hooks.runFirstActions
|
|||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
internal const val PACKAGE_NAME_QQ = "com.tencent.mobileqq"
|
private const val PACKAGE_NAME_QQ = "com.tencent.mobileqq"
|
||||||
internal const val PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi"
|
private const val PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi"
|
||||||
internal const val PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite"
|
private const val PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite"
|
||||||
internal const val PACKAGE_NAME_TIM = "com.tencent.tim"
|
private const val PACKAGE_NAME_TIM = "com.tencent.tim"
|
||||||
|
|
||||||
|
private val uselessProcess = listOf("peak", "tool", "mini", "qzone")
|
||||||
|
|
||||||
internal class XposedEntry: IXposedHookLoadPackage {
|
internal class XposedEntry: IXposedHookLoadPackage {
|
||||||
companion object {
|
companion object {
|
||||||
@ -121,9 +127,7 @@ internal class XposedEntry: IXposedHookLoadPackage {
|
|||||||
System.setProperty("qxbot_flag", "1")
|
System.setProperty("qxbot_flag", "1")
|
||||||
} else return
|
} else return
|
||||||
|
|
||||||
log("Process Name = " + MobileQQ.getMobileQQ().qqProcessName)
|
val processName = MobileQQ.getMobileQQ().qqProcessName
|
||||||
|
|
||||||
PlatformUtils.isTim()
|
|
||||||
|
|
||||||
// MSG LISTENER 进程运行在主进程
|
// MSG LISTENER 进程运行在主进程
|
||||||
// API 也应该开放在主进程
|
// API 也应该开放在主进程
|
||||||
@ -134,6 +138,22 @@ internal class XposedEntry: IXposedHookLoadPackage {
|
|||||||
MMKVFetcher.initMMKV(ctx)
|
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)
|
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.IKernelService
|
||||||
import com.tencent.qqnt.kernel.api.impl.MsgService
|
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.IOperateCallback
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.IQQNTWrapperSession
|
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.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.remote.service.PacketReceiver
|
import moe.fuqiuluo.shamrock.remote.service.PacketReceiver
|
||||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
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.remote.service.listener.PrimitiveListener
|
||||||
import moe.fuqiuluo.shamrock.tools.hookMethod
|
import moe.fuqiuluo.shamrock.tools.hookMethod
|
||||||
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||||
import kotlin.reflect.jvm.javaMethod
|
|
||||||
|
|
||||||
internal object NTServiceFetcher {
|
internal object NTServiceFetcher {
|
||||||
private lateinit var iKernelService: IKernelService
|
private lateinit var iKernelService: IKernelService
|
||||||
@ -30,7 +23,7 @@ internal object NTServiceFetcher {
|
|||||||
lock.withLock {
|
lock.withLock {
|
||||||
val msgService = service.msgService ?: return
|
val msgService = service.msgService ?: return
|
||||||
val sessionService = service.wrapperSession ?: return
|
val sessionService = service.wrapperSession ?: return
|
||||||
val groupService = sessionService.groupService ?: return
|
//val groupService = sessionService.groupService ?: return
|
||||||
|
|
||||||
val curHash = service.hashCode() + msgService.hashCode()
|
val curHash = service.hashCode() + msgService.hashCode()
|
||||||
if (isInitForNt(curHash)) return
|
if (isInitForNt(curHash)) return
|
||||||
@ -43,7 +36,7 @@ internal object NTServiceFetcher {
|
|||||||
this.iKernelService = service
|
this.iKernelService = service
|
||||||
|
|
||||||
|
|
||||||
initNTKernelListener(msgService, groupService)
|
initNTKernelListener(msgService)
|
||||||
antiBackgroundMode(sessionService)
|
antiBackgroundMode(sessionService)
|
||||||
//hookGuildListener(sessionService)
|
//hookGuildListener(sessionService)
|
||||||
}
|
}
|
||||||
@ -66,7 +59,7 @@ internal object NTServiceFetcher {
|
|||||||
return hash == curKernelHash
|
return hash == curKernelHash
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initNTKernelListener(msgService: MsgService, groupService: IKernelGroupService) {
|
private fun initNTKernelListener(msgService: MsgService) {
|
||||||
if (!PlatformUtils.isMainProcess()) return
|
if (!PlatformUtils.isMainProcess()) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,12 +1,42 @@
|
|||||||
|
@file:Suppress("UNUSED_VARIABLE", "LocalVariableName")
|
||||||
|
|
||||||
package moe.fuqiuluo.shamrock.xposed.hooks
|
package moe.fuqiuluo.shamrock.xposed.hooks
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.tencent.mobileqq.perf.block.BinderMethodProxy
|
||||||
|
import com.tencent.mobileqq.qmmkv.MMKVOptionEntity
|
||||||
|
import de.robv.android.xposed.XposedBridge
|
||||||
|
import epic.EIPCClient
|
||||||
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
|
import moe.fuqiuluo.shamrock.tools.beforeHook
|
||||||
|
import moe.fuqiuluo.shamrock.tools.hookMethod
|
||||||
|
import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader
|
||||||
|
import moe.fuqiuluo.symbols.Process
|
||||||
import moe.fuqiuluo.symbols.XposedHook
|
import moe.fuqiuluo.symbols.XposedHook
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
@XposedHook(priority = -1)
|
@XposedHook(priority = -1, process = Process.ALL)
|
||||||
internal class HookForDebug: IAction {
|
internal class HookForDebug: IAction {
|
||||||
override fun invoke(ctx: Context) {
|
override fun invoke(ctx: Context) {
|
||||||
/*
|
/*val NtDnsManager = LuoClassloader.load("com.tencent.qqnt.dns.NtDnsManager")!!
|
||||||
|
val NtDnsInternal = NtDnsManager.declaredMethods.first {
|
||||||
|
!Modifier.isStatic(it.modifiers) && it.parameterCount == 0
|
||||||
|
}.returnType
|
||||||
|
XposedBridge.hookMethod(NtDnsInternal.declaredMethods.first {
|
||||||
|
it.parameterCount == 2
|
||||||
|
&& it.parameterTypes[0] == String::class.java
|
||||||
|
&& it.parameterTypes[1] == Int::class.java
|
||||||
|
&& it.returnType == ArrayList::class.java
|
||||||
|
}, beforeHook {
|
||||||
|
val domain = it.args[0] as String
|
||||||
|
val type = it.args[1] as Int
|
||||||
|
LogCenter.log("NtDnsManager: reqDomain2IpList($domain, $type)")
|
||||||
|
LogCenter.log(Exception().stackTraceToString())
|
||||||
|
})*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
val httpEngineService = AppRuntimeFetcher.appRuntime
|
val httpEngineService = AppRuntimeFetcher.appRuntime
|
||||||
.getRuntimeService(IHttpEngineService::class.java, "all")
|
.getRuntimeService(IHttpEngineService::class.java, "all")
|
||||||
httpEngineService.javaClass.hookMethod("sendReq").before {
|
httpEngineService.javaClass.hookMethod("sendReq").before {
|
||||||
@ -19,7 +49,17 @@ internal class HookForDebug: IAction {
|
|||||||
LogCenter.log("请求地址: ${req.mReqUrl}")
|
LogCenter.log("请求地址: ${req.mReqUrl}")
|
||||||
LogCenter.log("请求: ${req.toInnerValuesString(NetReq::class.java)}")
|
LogCenter.log("请求: ${req.toInnerValuesString(NetReq::class.java)}")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
BinderMethodProxy::class.java.hookMethod("callServer").before {
|
||||||
|
val action = it.args[2] as String
|
||||||
|
if (action == "reqDomain2IpList") {
|
||||||
|
LogCenter.log(Exception().stackTraceToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EIPCClient::class.java.hookMethod("callServer").before {
|
||||||
|
val module = it.args[0] as String
|
||||||
|
val action = it.args[1] as String
|
||||||
|
if (action == "reqDomain2IpList" || module.contains("dns", ignoreCase = true)) {
|
||||||
|
LogCenter.log(Exception().stackTraceToString())
|
||||||
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user