This commit is contained in:
2024-07-07 09:53:02 +08:00
12 changed files with 267 additions and 83 deletions

View File

@@ -21,6 +21,8 @@
package cc.hicore.hook; package cc.hicore.hook;
import static io.github.qauxv.util.HostInfo.requireMinQQVersion;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import cc.ioctl.util.HookUtils; import cc.ioctl.util.HookUtils;
@@ -31,6 +33,7 @@ import io.github.qauxv.bridge.AppRuntimeHelper;
import io.github.qauxv.dsl.FunctionEntryRouter; import io.github.qauxv.dsl.FunctionEntryRouter;
import io.github.qauxv.hook.CommonSwitchFunctionHook; import io.github.qauxv.hook.CommonSwitchFunctionHook;
import io.github.qauxv.util.Initiator; import io.github.qauxv.util.Initiator;
import io.github.qauxv.util.QQVersion;
@FunctionHookEntry @FunctionHookEntry
@UiItemAgentEntry @UiItemAgentEntry
@@ -55,6 +58,20 @@ public class ShowAccurateGaggedTime extends CommonSwitchFunctionHook {
@Override @Override
protected boolean initOnce() throws Exception { protected boolean initOnce() throws Exception {
if (requireMinQQVersion(QQVersion.QQ_9_0_75)) {
HookUtils.hookBeforeIfEnabled(this,
Reflex.findMethod(Initiator.loadClass("com.tencent.qqnt.troop.impl.TroopGagUtils"), String.class,
"remainingTimeToStringCountDown", long.class), param -> {
long time = (long) param.args[0];
if (time <= 0) {
param.setResult("[0秒]");
return;
}
param.setResult(secondToTime(time));
});
return true;
}
HookUtils.hookBeforeIfEnabled(this, HookUtils.hookBeforeIfEnabled(this,
Reflex.findMethod(Initiator.loadClass("com.tencent.mobileqq.troop.troopgag.api.impl.TroopGagServiceImpl"), String.class, Reflex.findMethod(Initiator.loadClass("com.tencent.mobileqq.troop.troopgag.api.impl.TroopGagServiceImpl"), String.class,
"gagTimeToStringCountDown", Context.class, long.class), param -> { "gagTimeToStringCountDown", Context.class, long.class), param -> {

View File

@@ -23,6 +23,7 @@
package cc.ioctl.hook.chat package cc.ioctl.hook.chat
import cc.hicore.QApp.QAppUtils import cc.hicore.QApp.QAppUtils
import cc.ioctl.util.hookAfterIfEnabled
import cc.ioctl.util.hookBeforeIfEnabled import cc.ioctl.util.hookBeforeIfEnabled
import io.github.qauxv.base.annotation.FunctionHookEntry import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry import io.github.qauxv.base.annotation.UiItemAgentEntry
@@ -36,9 +37,14 @@ import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.hook.CommonSwitchFunctionHook import io.github.qauxv.hook.CommonSwitchFunctionHook
import io.github.qauxv.util.Initiator import io.github.qauxv.util.Initiator
import io.github.qauxv.util.Log import io.github.qauxv.util.Log
import io.github.qauxv.util.QQVersion
import io.github.qauxv.util.SyncUtils import io.github.qauxv.util.SyncUtils
import io.github.qauxv.util.dexkit.CMessageRecordFactory import io.github.qauxv.util.dexkit.CMessageRecordFactory
import io.github.qauxv.util.dexkit.DexKit
import io.github.qauxv.util.dexkit.Hd_GagInfoDisclosure_Method
import io.github.qauxv.util.dexkit.NContactUtils_getBuddyName
import io.github.qauxv.util.dexkit.NContactUtils_getDiscussionMemberShowName import io.github.qauxv.util.dexkit.NContactUtils_getDiscussionMemberShowName
import io.github.qauxv.util.requireMinQQVersion
import xyz.nextalone.util.get import xyz.nextalone.util.get
@FunctionHookEntry @FunctionHookEntry
@@ -49,6 +55,8 @@ object GagInfoDisclosure : CommonSwitchFunctionHook(
targets = arrayOf( targets = arrayOf(
CMessageRecordFactory, CMessageRecordFactory,
NContactUtils_getDiscussionMemberShowName, NContactUtils_getDiscussionMemberShowName,
NContactUtils_getBuddyName,
Hd_GagInfoDisclosure_Method,
) )
) { ) {
@@ -57,6 +65,10 @@ object GagInfoDisclosure : CommonSwitchFunctionHook(
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.CHAT_CATEGORY override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.CHAT_CATEGORY
override val isAvailable = QAppUtils.isQQnt() override val isAvailable = QAppUtils.isQQnt()
private fun getLongData(bArr: ByteArray, i: Int): Long {
return (((bArr[i].toInt() and 255) shl 24) + ((bArr[i + 1].toInt() and 255) shl 16) + ((bArr[i + 2].toInt() and 255) shl 8) + ((bArr[i + 3].toInt() and 255) shl 0)).toLong()
}
private fun getSecStr(sec: Long): String { private fun getSecStr(sec: Long): String {
val (min, hour, day) = Triple("", "", "") val (min, hour, day) = Triple("", "", "")
val d = sec / 86400 val d = sec / 86400
@@ -70,48 +82,31 @@ object GagInfoDisclosure : CommonSwitchFunctionHook(
} }
private fun NtGrayTipHelper.NtGrayTipJsonBuilder.appendUserItem(uin: String, name: String) { private fun NtGrayTipHelper.NtGrayTipJsonBuilder.appendUserItem(uin: String, name: String) {
val uid = RelationNTUinAndUidApi.getUidFromUin(uin).takeIf { it.isNullOrEmpty() } ?: "u_0000000000000000000000" val uid = RelationNTUinAndUidApi.getUidFromUin(uin).takeIf { it.isNullOrEmpty().not() } ?: "u_0000000000000000000000"
this.append(NtGrayTipHelper.NtGrayTipJsonBuilder.UserItem(uin, uid, name)) this.append(NtGrayTipHelper.NtGrayTipJsonBuilder.UserItem(uin, uid, name))
} }
override fun initOnce(): Boolean { private fun addGagTipMsg(selfUin: String, troopUin: String, opUin: String, victimUin: String, victimTime: Long) {
val clzGagMgr = Initiator._TroopGagMgr() val opName = ContactUtils.getTroopMemberNick(troopUin, opUin)
val method = clzGagMgr.declaredMethods.single { method -> val victimName = ContactUtils.getTroopMemberNick(troopUin, victimUin)
val params = method.parameterTypes; params.size == 5
&& params[0] == Int::class.java
&& params[1] == Long::class.java
&& params[2] == Long::class.java
&& params[3] == Long::class.java
&& params[4] == ArrayList::class.java
&& method.returnType == Void.TYPE
}
hookBeforeIfEnabled(method) { param ->
val selfUin = AppRuntimeHelper.getAccount()
val troopUin = param.args[1].toString()
val opUin = param.args[2].toString()
val opName = ContactUtils.getDiscussionMemberShowName(AppRuntimeHelper.getAppRuntime()!!, troopUin, opUin)
val pushParams = param.args[4] as ArrayList<*>
val victimUin = pushParams[0].get("uin") as String
val victimName = ContactUtils.getDiscussionMemberShowName(AppRuntimeHelper.getAppRuntime()!!, troopUin, victimUin)
val victimTime = pushParams[0].get("gagLength") as Long
val builder = NtGrayTipHelper.NtGrayTipJsonBuilder() val builder = NtGrayTipHelper.NtGrayTipJsonBuilder()
when (victimUin) { when (victimUin) {
"0" -> { "0" -> {
if (opUin == selfUin) builder.appendUserItem(selfUin, "") else builder.appendUserItem(opUin, "$opName") if (opUin == selfUin) builder.appendUserItem(selfUin, "") else builder.appendUserItem(opUin, opName)
builder.appendText(if (victimTime == 0L) "关闭了全员禁言" else "开启了全员禁言") builder.appendText(if (victimTime == 0L) "关闭了全员禁言" else "开启了全员禁言")
} }
selfUin -> { selfUin -> {
builder.appendUserItem(selfUin, "") builder.appendUserItem(selfUin, "")
builder.appendText("") builder.appendText("")
builder.appendUserItem(opUin, "$opName") builder.appendUserItem(opUin, opName)
builder.appendText(if (victimTime == 0L) "解除禁言" else "禁言${getSecStr(victimTime)}") builder.appendText(if (victimTime == 0L) "解除禁言" else "禁言${getSecStr(victimTime)}")
} }
else -> { else -> {
builder.appendUserItem(victimUin, "$victimName") builder.appendUserItem(victimUin, victimName)
builder.appendText("") builder.appendText("")
if (opUin == selfUin) builder.appendUserItem(selfUin, "") else builder.appendUserItem(opUin, "$opName") if (opUin == selfUin) builder.appendUserItem(selfUin, "") else builder.appendUserItem(opUin, opName)
builder.appendText(if (victimTime == 0L) "解除禁言" else "禁言${getSecStr(victimTime)}") builder.appendText(if (victimTime == 0L) "解除禁言" else "禁言${getSecStr(victimTime)}")
} }
} }
@@ -127,6 +122,43 @@ object GagInfoDisclosure : CommonSwitchFunctionHook(
} }
} }
} }
override fun initOnce(): Boolean {
if (requireMinQQVersion(QQVersion.QQ_9_0_73)) {
hookAfterIfEnabled(DexKit.requireMethodFromCache(Hd_GagInfoDisclosure_Method)) { param ->
val msgInfo = param.args[1]
val vMsg = msgInfo.get("vMsg") as ByteArray? ?: return@hookAfterIfEnabled
if (vMsg[4].toInt() == 12) {
val selfUin = AppRuntimeHelper.getAccount()
val troopUin = getLongData(vMsg, 0).toString()
val opUin = getLongData(vMsg, 6).toString()
val victimUinTmp = getLongData(vMsg, 16)
val victimUin = (victimUinTmp.takeIf { it > 0 } ?: (victimUinTmp and 0xFFFFFFFFL)).toString()
val victimTime = getLongData(vMsg, 20)
addGagTipMsg(selfUin, troopUin, opUin, victimUin, victimTime)
}
}
} else {
val troopGagClass = Initiator.loadClass("com.tencent.mobileqq.troop.utils.TroopGagMgr")
val method = troopGagClass.declaredMethods.single { method ->
val params = method.parameterTypes; params.size == 5
&& params[0] == Int::class.java
&& params[1] == Long::class.java
&& params[2] == Long::class.java
&& params[3] == Long::class.java
&& params[4] == ArrayList::class.java
&& method.returnType == Void.TYPE
}
hookBeforeIfEnabled(method) { param ->
val selfUin = AppRuntimeHelper.getAccount()
val troopUin = param.args[1].toString()
val opUin = param.args[2].toString()
val pushParams = param.args[4] as ArrayList<*>
val victimUin = pushParams[0].get("uin") as String
val victimTime = pushParams[0].get("gagLength") as Long
addGagTipMsg(selfUin, troopUin, opUin, victimUin, victimTime)
}
}
return true return true
} }
} }

View File

@@ -32,6 +32,7 @@ import cc.ioctl.util.HookUtils
import com.github.kyuubiran.ezxhelper.utils.isAbstract import com.github.kyuubiran.ezxhelper.utils.isAbstract
import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XC_MethodHook
import io.github.duzhaokun123.hook.MessageCopyHook import io.github.duzhaokun123.hook.MessageCopyHook
import io.github.duzhaokun123.hook.MessageTTSHook
import io.github.qauxv.base.annotation.FunctionHookEntry import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.hook.BasePersistBackgroundHook import io.github.qauxv.hook.BasePersistBackgroundHook
import io.github.qauxv.util.Initiator import io.github.qauxv.util.Initiator
@@ -52,7 +53,8 @@ object MenuBuilderHook : BasePersistBackgroundHook() {
MessageCopyHook, MessageCopyHook,
PicCopyToClipboard, PicCopyToClipboard,
MiniAppDirectJump, MiniAppDirectJump,
CopyMarkdown CopyMarkdown,
MessageTTSHook,
) )
override fun initOnce(): Boolean { override fun initOnce(): Boolean {

View File

@@ -22,6 +22,7 @@
package com.xiaoniu.hook package com.xiaoniu.hook
import com.github.kyuubiran.ezxhelper.utils.hookBefore
import com.github.kyuubiran.ezxhelper.utils.hookReturnConstant import com.github.kyuubiran.ezxhelper.utils.hookReturnConstant
import io.github.qauxv.base.annotation.FunctionHookEntry import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry import io.github.qauxv.base.annotation.UiItemAgentEntry
@@ -29,7 +30,9 @@ import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.hook.CommonSwitchFunctionHook import io.github.qauxv.hook.CommonSwitchFunctionHook
import io.github.qauxv.util.QQVersion import io.github.qauxv.util.QQVersion
import io.github.qauxv.util.requireMinQQVersion import io.github.qauxv.util.requireMinQQVersion
import xyz.nextalone.util.clazz
import xyz.nextalone.util.method import xyz.nextalone.util.method
import xyz.nextalone.util.set
import xyz.nextalone.util.throwOrTrue import xyz.nextalone.util.throwOrTrue
@FunctionHookEntry @FunctionHookEntry
@@ -38,6 +41,15 @@ object DisableTroopFlame : CommonSwitchFunctionHook() {
override val name = "屏蔽群火苗" override val name = "屏蔽群火苗"
override fun initOnce() = throwOrTrue { override fun initOnce() = throwOrTrue {
if (requireMinQQVersion(QQVersion.QQ_9_0_75)) {
// Waiting for a better solution...
"com.tencent.mobileqq.troop.flame.api.impl.TroopFlameApiImpl".clazz!!
.method("getFlameViewDataFromPB")!!
.hookBefore {
it.args[0].set("switchState", 0)
it.args[0].set("state", 0)
}
} else
"Lcom/tencent/mobileqq/troop/flame/api/impl/TroopFlameApiImpl;->getGroupExtFlameData(Lcom/tencent/mobileqq/data/troop/TroopInfoExt;)Ltencent/trpcprotocol/IqunFlameManageSvrPB\$GroupExtFlameData;" "Lcom/tencent/mobileqq/troop/flame/api/impl/TroopFlameApiImpl;->getGroupExtFlameData(Lcom/tencent/mobileqq/data/troop/TroopInfoExt;)Ltencent/trpcprotocol/IqunFlameManageSvrPB\$GroupExtFlameData;"
.method .method
.hookReturnConstant(null) .hookReturnConstant(null)

View File

@@ -26,25 +26,37 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import android.view.View import android.view.View
import cc.hicore.QApp.QAppUtils
import cc.ioctl.util.HostInfo import cc.ioctl.util.HostInfo
import cc.ioctl.util.Reflex import cc.ioctl.util.Reflex
import cc.ioctl.util.afterHookIfEnabled import cc.ioctl.util.afterHookIfEnabled
import com.xiaoniu.dispatcher.OnMenuBuilder
import com.xiaoniu.util.ContextUtils
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers
import io.github.duzhaokun123.hook.MessageCopyHook.TAG
import io.github.duzhaokun123.util.TTS import io.github.duzhaokun123.util.TTS
import io.github.qauxv.R import io.github.qauxv.R
import io.github.qauxv.base.annotation.FunctionHookEntry import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.dsl.FunctionEntryRouter import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.hook.CommonSwitchFunctionHook import io.github.qauxv.hook.CommonSwitchFunctionHook
import io.github.qauxv.step.Step
import io.github.qauxv.ui.CommonContextWrapper import io.github.qauxv.ui.CommonContextWrapper
import io.github.qauxv.util.CustomMenu import io.github.qauxv.util.CustomMenu
import io.github.qauxv.util.Initiator import io.github.qauxv.util.Initiator
import io.github.qauxv.util.QQVersion
import io.github.qauxv.util.Toasts import io.github.qauxv.util.Toasts
import io.github.qauxv.util.dexkit.DexDeobfsProvider
import io.github.qauxv.util.dexkit.DexKit
import io.github.qauxv.util.dexkit.DexKitFinder
import io.github.qauxv.util.dexkit.TextMsgItem_getText
import java.lang.reflect.Modifier
@FunctionHookEntry @FunctionHookEntry
@UiItemAgentEntry @UiItemAgentEntry
object MessageTTSHook : CommonSwitchFunctionHook() { object MessageTTSHook : CommonSwitchFunctionHook(), OnMenuBuilder, DexKitFinder {
override val name: String override val name: String
get() = "文字消息转语音 (使用系统 TTS)" get() = "文字消息转语音 (使用系统 TTS)"
@@ -52,6 +64,8 @@ object MessageTTSHook : CommonSwitchFunctionHook() {
get() = "提示失败多半是没设置系统 TTS 引擎" get() = "提示失败多半是没设置系统 TTS 引擎"
override fun initOnce(): Boolean { override fun initOnce(): Boolean {
if (QAppUtils.isQQnt()) return true
val cl_ArkAppItemBuilder = Initiator._TextItemBuilder() val cl_ArkAppItemBuilder = Initiator._TextItemBuilder()
XposedHelpers.findAndHookMethod( XposedHelpers.findAndHookMethod(
cl_ArkAppItemBuilder, "a", Int::class.javaPrimitiveType, Context::class.java, cl_ArkAppItemBuilder, "a", Int::class.javaPrimitiveType, Context::class.java,
@@ -106,11 +120,93 @@ object MessageTTSHook : CommonSwitchFunctionHook() {
R.id.item_tts -> { R.id.item_tts -> {
TTS.speak(wc, Reflex.getInstanceObjectOrNull(chatMessage, "msg")?.toString() ?: "") TTS.speak(wc, Reflex.getInstanceObjectOrNull(chatMessage, "msg")?.toString() ?: "")
} }
R.id.item_tts2 -> { R.id.item_tts2 -> {
val msg = Reflex.getInstanceObjectOrNull(chatMessage, "msg")?.toString() ?: "" val msg = Reflex.getInstanceObjectOrNull(chatMessage, "msg")?.toString() ?: ""
TTS.showConfigDialog(wc, msg) TTS.showConfigDialog(wc, msg)
} }
} }
} }
override val targetComponentTypes: Array<String>
get() = arrayOf("com.tencent.mobileqq.aio.msglist.holder.component.text.AIOTextContentComponent")
override fun onGetMenuNt(msg: Any, componentType: String, param: XC_MethodHook.MethodHookParam) {
if (!isEnabled) return
val item = CustomMenu.createItemIconNt(msg, "TTS", 0, R.id.item_tts) {
val ctx = ContextUtils.getCurrentActivity()
val wc = CommonContextWrapper.createAppCompatContext(ctx)
val text = try {
DexKit.requireMethodFromCache(TextMsgItem_getText).also {
it.isAccessible = true
}.invoke(msg) as CharSequence
} catch (e: Exception) {
"${e.javaClass.name}: ${e.message}\n" + (e.stackTrace.joinToString("\n"))
}
TTS.speak(wc, text.toString())
}
val item2 = CustomMenu.createItemIconNt(msg, "TTS+", 0, R.id.item_tts2) {
val ctx = ContextUtils.getCurrentActivity()
val wc = CommonContextWrapper.createAppCompatContext(ctx)
val text = try {
DexKit.requireMethodFromCache(TextMsgItem_getText).also {
it.isAccessible = true
}.invoke(msg) as CharSequence
} catch (e: Exception) {
"${e.javaClass.name}: ${e.message}\n" + (e.stackTrace.joinToString("\n"))
}
TTS.showConfigDialog(wc, text.toString())
}
param.result = (param.result as List<*>) + item + item2
}
/**
* see MessageCopyHook
*/
override fun makePreparationSteps(): Array<Step> {
return arrayOf(object : Step {
override fun step(): Boolean {
return doFind()
}
override fun isDone(): Boolean {
return !isNeedFind
}
override fun getPriority(): Int {
return 0
}
override fun getDescription(): String {
return "文本消息相关方法查找中"
}
})
}
override val isNeedFind: Boolean
get() = HostInfo.requireMinQQVersion(QQVersion.QQ_8_9_63) && TextMsgItem_getText.descCache == null
override fun doFind(): Boolean {
DexDeobfsProvider.getCurrentBackend().use { backend ->
val dexKit = backend.getDexKitBridge()
android.util.Log.d(TAG, "doFind: doFind")
val getText = dexKit.findMethod {
searchPackages("com.tencent.mobileqq.aio.msg")
matcher {
modifiers = Modifier.PRIVATE
returnType = "java.lang.CharSequence"
paramCount = 0
usingNumbers(24)
usingStrings("biz_src_jc_aio")
// addCall {
// name = "getQQText"
// }
}
}.firstOrNull() ?: return false
android.util.Log.d(TAG, "doFind: $getText")
TextMsgItem_getText.descCache = getText.descriptor
}
return true
}
} }

View File

@@ -136,6 +136,7 @@ object TTS {
} }
fun showConfigDialog(wc: Context, text: String = "") { fun showConfigDialog(wc: Context, text: String = "") {
if (!checkInit(wc)) return
val binding = Tts2DialogBinding.inflate(LayoutInflater.from(wc)) val binding = Tts2DialogBinding.inflate(LayoutInflater.from(wc))
binding.etMsg.setText(text) binding.etMsg.setText(text)
binding.tvVoice.text = instance.voice?.toString() ?: "null" binding.tvVoice.text = instance.voice?.toString() ?: "null"

View File

@@ -29,8 +29,10 @@ import com.tencent.common.app.AppInterface;
import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedHelpers;
import io.github.qauxv.base.annotation.DexDeobfs; import io.github.qauxv.base.annotation.DexDeobfs;
import io.github.qauxv.bridge.ntapi.RelationNTUinAndUidApi; import io.github.qauxv.bridge.ntapi.RelationNTUinAndUidApi;
import io.github.qauxv.util.HostInfo;
import io.github.qauxv.util.Initiator; import io.github.qauxv.util.Initiator;
import io.github.qauxv.util.Log; import io.github.qauxv.util.Log;
import io.github.qauxv.util.QQVersion;
import io.github.qauxv.util.dexkit.DexKit; import io.github.qauxv.util.dexkit.DexKit;
import io.github.qauxv.util.dexkit.NContactUtils_getBuddyName; import io.github.qauxv.util.dexkit.NContactUtils_getBuddyName;
import io.github.qauxv.util.dexkit.NContactUtils_getDiscussionMemberShowName; import io.github.qauxv.util.dexkit.NContactUtils_getDiscussionMemberShowName;
@@ -59,6 +61,7 @@ public class ContactUtils {
Objects.requireNonNull(memberUin); Objects.requireNonNull(memberUin);
AppRuntime app = AppRuntimeHelper.getQQAppInterface(); AppRuntime app = AppRuntimeHelper.getQQAppInterface();
assert app != null; assert app != null;
if (!HostInfo.requireMinQQVersion(QQVersion.QQ_9_0_25)) {
try { try {
Object mTroopManager = ManagerHelper.getTroopManager(); Object mTroopManager = ManagerHelper.getTroopManager();
Object troopMemberInfo = Reflex.invokeVirtualDeclaredOrdinal(mTroopManager, 0, 3, false, Object troopMemberInfo = Reflex.invokeVirtualDeclaredOrdinal(mTroopManager, 0, 3, false,
@@ -77,6 +80,7 @@ public class ContactUtils {
} catch (Exception | LinkageError e) { } catch (Exception | LinkageError e) {
Log.e(e); Log.e(e);
} }
}
try { try {
String ret = getDiscussionMemberShowName(app, troopUin, memberUin); String ret = getDiscussionMemberShowName(app, troopUin, memberUin);
if (ret != null) { if (ret != null) {

View File

@@ -125,4 +125,6 @@ public class QQVersion {
public static final long QQ_9_0_68 = 6612; public static final long QQ_9_0_68 = 6612;
public static final long QQ_9_0_70 = 6676; public static final long QQ_9_0_70 = 6676;
public static final long QQ_9_0_71 = 6702; public static final long QQ_9_0_71 = 6702;
public static final long QQ_9_0_73 = 6722;
public static final long QQ_9_0_75 = 6786;
} }

View File

@@ -995,3 +995,10 @@ data object Hd_DisableGrowHalfLayer_Method : DexKitTarget.UsingStringVector() {
m.returnType == Void.TYPE && m.paramCount == 3 m.returnType == Void.TYPE && m.paramCount == 3
} }
} }
data object Hd_GagInfoDisclosure_Method : DexKitTarget.UsingStr() {
override val findMethod = true
override val traitString = arrayOf("<---0x2dc push groupCode:")
override val declaringClass = "com.tencent.imcore.message"
override val filter = DexKitFilter.strInClsName("com/tencent/imcore/message/")
}

View File

@@ -97,6 +97,7 @@ object FakePicSize : BaseFunctionHook(
val msgServiceClass = Initiator.loadClass("com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService\$CppProxy") val msgServiceClass = Initiator.loadClass("com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService\$CppProxy")
hookBeforeIfEnabled(msgServiceClass.method("sendMsg")!!) { param -> hookBeforeIfEnabled(msgServiceClass.method("sendMsg")!!) { param ->
val size = sizeMap.values.toList()[sizeIndex] val size = sizeMap.values.toList()[sizeIndex]
if (size == 0) return@hookBeforeIfEnabled
val elements = param.args[2] as ArrayList<*> val elements = param.args[2] as ArrayList<*>
for (element in elements) { for (element in elements) {
val msgElement = (element as MsgElement) val msgElement = (element as MsgElement)

View File

@@ -45,6 +45,7 @@ import cc.ioctl.util.HostInfo
import cc.ioctl.util.Reflex import cc.ioctl.util.Reflex
import cc.ioctl.util.hookAfterIfEnabled import cc.ioctl.util.hookAfterIfEnabled
import cc.ioctl.util.hookBeforeIfEnabled import cc.ioctl.util.hookBeforeIfEnabled
import com.github.kyuubiran.ezxhelper.utils.paramCount
import io.github.qauxv.base.annotation.FunctionHookEntry import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.dsl.FunctionEntryRouter import io.github.qauxv.dsl.FunctionEntryRouter
@@ -100,11 +101,11 @@ object MessagingStyleNotification : CommonSwitchFunctionHook(SyncUtils.PROC_ANY)
lateinit var buildNotification: Method lateinit var buildNotification: Method
lateinit var recentInfoBuilder: Method lateinit var recentInfoBuilder: Method
cNotificationFacade.declaredMethods.forEach { cNotificationFacade.declaredMethods.forEach {
if (it.parameterTypes.size != 3) return@forEach if (it.paramCount < 3 || it.parameterTypes[0] != cAppRuntime) return@forEach
if (it.parameterTypes[0] != cAppRuntime) return@forEach if (it.paramCount == 3 && it.parameterTypes[2] == cCommonInfo) {
if (it.parameterTypes[2] == cCommonInfo) {
buildNotification = it buildNotification = it
} else if (it.parameterTypes[1] == cRecentInfo) { } else if (it.paramCount >= 3 && it.parameterTypes[1] == cRecentInfo && it.parameterTypes[2] == Boolean::class.java) {
// paramCount=4 since 9.0.75
recentInfoBuilder = it recentInfoBuilder = it
} }
} }
@@ -131,7 +132,8 @@ object MessagingStyleNotification : CommonSwitchFunctionHook(SyncUtils.PROC_ANY)
val info = QQRecentContactInfo(pair.first) val info = QQRecentContactInfo(pair.first)
notificationInfoMap.remove(oldNotification.contentIntent) notificationInfoMap.remove(oldNotification.contentIntent)
if (info.chatType == 1 || info.chatType == 2) { if (info.chatType == 1 || info.chatType == 2) {
val content = info.abstractContent?.joinToString(separator = "") { it.get("content", String::class.java) ?: "[未解析消息]" } ?: return@hookBeforeIfEnabled val content =
info.abstractContent?.joinToString(separator = "") { it.get("content", String::class.java) ?: "[未解析消息]" } ?: return@hookBeforeIfEnabled
val senderName = info.getUserName() ?: return@hookBeforeIfEnabled val senderName = info.getUserName() ?: return@hookBeforeIfEnabled
val senderUin = info.senderUin ?: return@hookBeforeIfEnabled val senderUin = info.senderUin ?: return@hookBeforeIfEnabled
val senderIcon: IconCompat val senderIcon: IconCompat
@@ -146,17 +148,23 @@ object MessagingStyleNotification : CommonSwitchFunctionHook(SyncUtils.PROC_ANY)
// 好友消息 // 好友消息
when (info.chatType) { when (info.chatType) {
1 -> { 1 -> {
senderIcon = IconCompat.createFromIcon(hostInfo.application, oldNotification.getLargeIcon()) ?: IconCompat.createWithBitmap(ResUtils.loadDrawableFromAsset("face.png", context).toBitmap()) senderIcon = IconCompat.createFromIcon(hostInfo.application, oldNotification.getLargeIcon())
?: IconCompat.createWithBitmap(ResUtils.loadDrawableFromAsset("face.png", context).toBitmap())
shortcut = getShortcut("private_$senderUin", senderName, senderIcon, pair.second) shortcut = getShortcut("private_$senderUin", senderName, senderIcon, pair.second)
} }
2 -> { 2 -> {
groupName = info.peerName ?: return@hookBeforeIfEnabled groupName = info.peerName ?: return@hookBeforeIfEnabled
groupUin = info.peerUin ?: return@hookBeforeIfEnabled groupUin = info.peerUin ?: return@hookBeforeIfEnabled
senderIcon = avatarHelper.getAvatar(senderUin.toString()) ?: IconCompat.createWithBitmap(ResUtils.loadDrawableFromAsset("face.png", context).toBitmap()) senderIcon = avatarHelper.getAvatar(senderUin.toString()) ?: IconCompat.createWithBitmap(
groupIcon = IconCompat.createFromIcon(hostInfo.application, oldNotification.getLargeIcon()) ?: IconCompat.createWithBitmap(ResUtils.loadDrawableFromAsset("face.png", context).toBitmap()) ResUtils.loadDrawableFromAsset("face.png", context).toBitmap()
)
groupIcon = IconCompat.createFromIcon(hostInfo.application, oldNotification.getLargeIcon())
?: IconCompat.createWithBitmap(ResUtils.loadDrawableFromAsset("face.png", context).toBitmap())
shortcut = getShortcut("group_$groupUin", groupName, groupIcon, pair.second) shortcut = getShortcut("group_$groupUin", groupName, groupIcon, pair.second)
} }
else -> { else -> {
// Impossible // Impossible
throw Error("what the hell?") throw Error("what the hell?")
@@ -238,11 +246,13 @@ object MessagingStyleNotification : CommonSwitchFunctionHook(SyncUtils.PROC_ANY)
var messageStyle = historyMessage["$channelId+$mainUin"] var messageStyle = historyMessage["$channelId+$mainUin"]
if (messageStyle == null) { if (messageStyle == null) {
messageStyle = MessagingStyle(Person.Builder() messageStyle = MessagingStyle(
Person.Builder()
.setName(mainName) .setName(mainName)
.setIcon(mainIcon) .setIcon(mainIcon)
.setKey(mainUin.toString()) .setKey(mainUin.toString())
.build()) .build()
)
messageStyle.conversationTitle = mainName messageStyle.conversationTitle = mainName
messageStyle.isGroupConversation = groupUin != null messageStyle.isGroupConversation = groupUin != null
historyMessage["$channelId+$mainUin"] = messageStyle historyMessage["$channelId+$mainUin"] = messageStyle

View File

@@ -64,7 +64,7 @@ object ChatInputHint : CommonConfigFunctionHook("na_chat_input_hint", arrayOf(NB
override fun initOnce(): Boolean = throwOrTrue { override fun initOnce(): Boolean = throwOrTrue {
if (requireMinQQVersion(QQVersion.QQ_8_9_63)) { if (requireMinQQVersion(QQVersion.QQ_8_9_63)) {
// 私聊 && QQ_9_0_50版本后的群聊 // 私聊 && QQ9.0.35版本后的群聊
DexKit.requireMethodFromCache(AIO_InputRootInit_QQNT).hookAfter(this) { DexKit.requireMethodFromCache(AIO_InputRootInit_QQNT).hookAfter(this) {
it.thisObject.javaClass.declaredFields.single { it.type == EditText::class.java }.apply { it.thisObject.javaClass.declaredFields.single { it.type == EditText::class.java }.apply {
isAccessible = true isAccessible = true
@@ -72,8 +72,8 @@ object ChatInputHint : CommonConfigFunctionHook("na_chat_input_hint", arrayOf(NB
et.hint = getValue() et.hint = getValue()
} }
} }
// QQ_9_0_50版本前的群聊 // QQ9.0.35版本前的群聊
if (!requireMinQQVersion(QQVersion.QQ_9_0_50)) { if (!requireMinQQVersion(QQVersion.QQ_9_0_35)) {
when { // Lcom/tencent/mobileqq/aio/input/anonymous/AnonymousModeInputVBDelegate;->setNotAnonymousHint()V when { // Lcom/tencent/mobileqq/aio/input/anonymous/AnonymousModeInputVBDelegate;->setNotAnonymousHint()V
requireMinQQVersion(QQVersion.QQ_9_0_30) -> "Lcom/tencent/mobileqq/aio/input/c/c;->l()V" requireMinQQVersion(QQVersion.QQ_9_0_30) -> "Lcom/tencent/mobileqq/aio/input/c/c;->l()V"
requireMinQQVersion(QQVersion.QQ_9_0_20) -> "Lcom/tencent/mobileqq/aio/input/b/c;->l()V" requireMinQQVersion(QQVersion.QQ_9_0_20) -> "Lcom/tencent/mobileqq/aio/input/b/c;->l()V"