Skip to content

Commit

Permalink
Merge branch 'cinit:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
suzhelan authored Dec 1, 2023
2 parents 8dd6320 + 54a7fe8 commit 7924d38
Show file tree
Hide file tree
Showing 17 changed files with 448 additions and 101 deletions.
152 changes: 143 additions & 9 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,152 @@
# 贡献指南

**首先,欢迎您为QAuxiliary这个项目做出贡献。**

## 分支约定

不管是直接 Push 代码还是提交 Pull Request,都必须使 commit 指向 dev 分支。
**首先,欢迎您为 QAuxiliary 这个项目做出贡献。**

## 写在前面

本项目由若干个功能组成,在大多数情况下,每个功能都是独立的,但是也有一些功能是依赖于其他功能的。
总体来说,每个功能都是由使用它的开发者维护的。这意味着,存在一些功能,由于没有开发者使用,而无人维护。
这很正常,本项目的初衷就是通过分享自己已经开发的功能,来省去其他有相同需求的人的重复劳动,而不是追求功能的多少。

由于本项目的特殊性,我并不推荐开发者在本项目上花费过多的时间,开发者只需要让自己需要的功能正常运行即可。
至于那些你自己都不用的功能,我并不推荐你去维护它,让真正需要它的人去维护就行了。

大可把开源项目当作一个并不重要的爱好,心情好就玩一玩,不想做了就不做,玩腻了就丢一边,没有人有资格去要求你做什么。
取悦自己,而不是为了没用的胜负欲去取悦别人。

## 如何添加一个新的功能

1. 如果你是第一次提交代码,先选一个 package, 通常是反写的域名加上一些其他信息,如 `com.example.hook`.
然后将这个 package 加入到 [proguard-rules.pro](../app/proguard-rules.pro) 中,以防止被 R8 优化掉。
2. 在 package 下新建一个类,一个功能一个类,类名应该是一个名词,如 `RemoveShakeAdExampleHook`, 按下面的模板编写功能代码。
写了类后,它就会自动被注册到功能列表中,如果用户启用了这个功能,那么这个类的 initOnce 方法将会自动调用。
3. 测试功能,记得去模块里打开你写的功能。由于 Android Studio 在 Android 11+ 默认启用部署优化(deployment optimization),
使用 JVMTI(ARTTI) 来热加载代码,这会导致实际并没有更新 apk,因此需要在 Android Studio 中禁用部署优化。
`Run` 菜单中选择 `Edit Configurations...`,在 `Android App` 标签页中,选择 `QAuxiliary.app`
`General` 标签页中,将 `Install Options` 中的 `Always install with package manager (disable install optimization)` 勾选上。
你也可以通过直接运行 `:app:installDebug` task 来安装应用,而不是通过 Android Studio 运行 app。

## 功能模板

如果你倾向于使用 Kotlin 来编写功能,可以参考以下模板.

```kotlin
package com.example.hook

import cc.ioctl.util.hookBeforeIfEnabled
import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.hook.CommonSwitchFunctionHook
import io.github.qauxv.util.Initiator
import io.github.qauxv.util.QQVersion
import io.github.qauxv.util.requireMinQQVersion

// FunctionHookEntry 和 UiItemAgentEntry 用于注册功能,这两个注解都是必要的
// 注意是 object,而不是 class
@FunctionHookEntry
@UiItemAgentEntry
object RemoveShakeAdExampleHook : CommonSwitchFunctionHook() {

override val name = "这里写功能名字"

override val description = "这里写功能描述"

override val uiItemLocation: Array<String> = FunctionEntryRouter.Locations.这里写功能位置

// isAvailable 可选,可以不写
override val isAvailable: Boolean get() = requireMinQQVersion(QQVersion.最低支持版本)

override fun initOnce(): Boolean {
// 在这里写功能初始化代码,如果用户启用了这个功能,那么这里的代码将会被执行一次
// initOnce 里可以随便 throw 异常, throw 的异常会显示在日志和故障排除方便定位问题
val klass = Initiator.loadClass("com.tencent.mobileqq.example.SomeClassManager")
// Initiator.loadClass 用于加载 QQ 的类,如果类不存在,将会 throw ClassNotFoundException
val someMethod = kTroopInfo.getDeclaredMethod("someMethod", Long::class.java)
// hookBeforeIfEnabled 只有在用户启用了这个功能的情况下才会执行 hook 回调
// 避免使用 XposedBridge 和 XposedHelpers,因为这些类在 new Xposed API 中不复存在
hookBeforeIfEnabled(someMethod) {
// 做一些操作,其中 it 是一个 HookParam 对象,可以通过 it.args 获取方法参数
it.result = 0L
}
// return true 表示初始化成功,false 或者抛异常表示初始化失败
return true
}
}
```

如果你倾向于使用 Java 来编写功能,可以参考以下模板.

```java
package com.example.hook;

import cc.ioctl.util.hookBeforeIfEnabled;
import io.github.qauxv.base.annotation.FunctionHookEntry;
import io.github.qauxv.base.annotation.UiItemAgentEntry;
import io.github.qauxv.dsl.FunctionEntryRouter;
import io.github.qauxv.hook.CommonSwitchFunctionHook;
import io.github.qauxv.util.Initiator;
import io.github.qauxv.util.QQVersion;
import io.github.qauxv.util.requireMinQQVersion;

// FunctionHookEntry 和 UiItemAgentEntry 用于注册功能,这两个注解都是必要的
@FunctionHookEntry
@UiItemAgentEntry
public final class RemoveShakeAdExampleHook extends CommonSwitchFunctionHook {

// INSTANCE 是必须的,因为 Java 没有 object,所以我们需要一个单例
public static final RemoveShakeAdExampleHook INSTANCE = new RemoveShakeAdExampleHook();

@Override
public String getName() {
return "这里写功能名字";
}

@Override
public String getDescription() {
return "这里写功能描述";
}

@Override
public String[] getUiItemLocation() {
return FunctionEntryRouter.Locations.这里写功能位置;
}

// isAvailable 可选,可以不写
@Override
public boolean isAvailable() {
return requireMinQQVersion(QQVersion.最低支持版本);
}

@Override
public boolean initOnce() throws Exception {
// 在这里写功能初始化代码,如果用户启用了这个功能,那么这里的代码将会被执行一次
// initOnce 里可以随便 throw 异常, throw 的异常会显示在日志和故障排除方便定位问题
Class<?> klass = Initiator.loadClass("com.tencent.mobileqq.example.SomeClassManager");
// Initiator.loadClass 用于加载 QQ 的类,如果类不存在,将会 throw ClassNotFoundException
Method someMethod = kTroopInfo.getDeclaredMethod("someMethod", Long.class);
// hookBeforeIfEnabled 只有在用户启用了这个功能的情况下才会执行 hook 回调
// 避免使用 XposedBridge 和 XposedHelpers,因为这些类在 new Xposed API 中不复存在
HookUtils.hookBeforeIfEnabled(this, someMethod, param -> {
// 做一些操作,其中 param 是一个 HookParam 对象,可以通过 param.args 获取方法参数
param.setResult(0L);
});
// return true 表示初始化成功,false 或者抛异常表示初始化失败
return true;
}
}
```

## Commit 相关

1. 禁止中文/拼音
1. 类名、变量名、方法名禁止中文/拼音(例外: 如果 QQ 的 API 本身就是拼音的, 那么就只能用拼音了)
2. 简洁明了
3. 一个commit做一件事情
4. 请勿在commit附上任何有关[skip ci]的字段
5. 每个commit都必须附着有效的GPG签名
3. 一个 commit 做一件事情
4. 请勿在 commit 附上任何有关 [skip ci] 的字段
5. 在 commit 之前请先更新到最新的 main 分支, 以方便我们进行快速合并(fast-forward merge).
6. 每个 commit 都必须附着有效的GPG签名,如果您不知道如何使用 GPG 签名,请参阅
[这里](https://docs.github.com/cn/github/authenticating-to-github/managing-commit-signature-verification).
如果你实在不会配置 GPG 签名,你仍然可以提交 PR, 但由于 main 分支要求所有 commit 必须附着有效的 GPG 签名,就只好由我们来代替你签名了.

## Pull Request

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
echo "sdk.dir=${ANDROID_HOME}" > local.properties
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.9.0
uses: gradle/gradle-build-action@v2.10.0

- name: Build with Gradle
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/push_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
restore-keys: native-cache-

- name: Setup Gradle
uses: gradle/gradle-build-action@v2.9.0
uses: gradle/gradle-build-action@v2.10.0

- name: Build with Gradle
run: |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,6 @@ QAuxiliary 将为分 `CI` 和 `推荐的CI` 两个版本
## 赞助

- 由于项目的特殊性,我们不接受任何形式的捐赠,但是我们希望有更多的人能够参与本项目的开发
- 如果您有兴趣参与本项目的开发,您可以参考[贡献指南](.github/CONTRIBUTING.md)

## [通用许可协议](https://github.com/qwq233/License/blob/master/v2/LICENSE.md)
1 change: 0 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
-dontwarn org.apache.bsf.*

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ object SimplifyQQSettingMe : MultiItemDelayableHook("SimplifyQQSettingMe") {
}
}
})

XposedBridge.hookAllMethods(ViewTreeObserver::class.java, "dispatchOnGlobalLayout", object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) {
try {
Expand All @@ -235,7 +235,7 @@ object SimplifyQQSettingMe : MultiItemDelayableHook("SimplifyQQSettingMe") {

// for NT QQ 8.9.68.11450
val clazz = Initiator.load(
if (requireMinQQVersion(QQVersion.QQ_8_9_90)) "com.tencent.mobileqq.QQSettingMeViewV9"
if (requireMinQQVersion(QQVersion.QQ_8_9_88)) "com.tencent.mobileqq.QQSettingMeViewV9"
else "com.tencent.mobileqq.activity.QQSettingMeViewV9"
)
clazz?.findAllMethods { paramCount == 1 && parameterTypes[0].name.contains("com.tencent.mobileqq.activity.qqsettingme") }?.hookAfter {
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/cc/ioctl/hook/ui/title/RemoveDailySign.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ object RemoveDailySign : CommonSwitchFunctionHook("kr_remove_daily_sign") {
val callback = HookUtils.afterIfEnabled(this) { param ->
// em_drawer_sign_up
val dailySignName = when {
requireMinQQVersion(QQVersion.QQ_8_9_90) -> "e0"
requireMinQQVersion(QQVersion.QQ_8_9_88) -> "h0"
requireMinQQVersion(QQVersion.QQ_8_9_70) -> "e0"
requireMinQQVersion(QQVersion.QQ_8_9_68) -> "h0"
requireMinQQVersion(QQVersion.QQ_8_9_28) -> "i0"
Expand Down Expand Up @@ -90,8 +92,16 @@ object RemoveDailySign : CommonSwitchFunctionHook("kr_remove_daily_sign") {
if (clazz != null) {
clazz.findField {
val cz = type
if (cz.name.contains("com.tencent.mobileqq.activity.qqsettingme.bizParts")) {
// private final com.tencent.mobileqq.bizParts.a F;
if (cz.name.contains(
when {
requireMinQQVersion(QQVersion.QQ_8_9_90) -> "com.tencent.mobileqq.bizParts"
else -> "com.tencent.mobileqq.activity.qqsettingme.bizParts"
}
)
) {
var i = 0
// 目标类里面的 LinearLayout 类型的参数一定大于2
for (f in cz.declaredFields) {
if (f.type == LinearLayout::class.java) {
i++
Expand All @@ -110,6 +120,7 @@ object RemoveDailySign : CommonSwitchFunctionHook("kr_remove_daily_sign") {
fields.add(f)
}
}
// private LinearLayout g;
it.thisObject.getObjectAs<LinearLayout>(fields[1].name, LinearLayout::class.java).setViewZeroSize()
}
}
Expand Down
25 changes: 25 additions & 0 deletions app/src/main/java/cc/ioctl/util/MsgRecordUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,21 @@ object MsgRecordUtil {
}
}

val NICK_BLOCKS = object : HashBiMap<String, String>() {
init {
put("VIP图标", "AIOVipIconProcessor")
put("额外VIP图标", "AIOVipIconExProcessor")
put("游戏图标", "AIOGameIconProcessor")
put("昵称", "MainNickNameBlock")
put("机器人图标", "NickNameRobotBlock")
put("群等级图标", "AIOTroopMemberLevelBlock")
put("群数字等级图标", "AIOTroopMemberGradeLevelBlock")
put("群荣耀图标", "AIOTroopHonorNickBlock")
put("群匿名图标", "AIOTroopAnonymousNickBlock")
put("群QQ圈图标", "QCircleTroopIconProcessor")
}
}

val MSG_WITH_DESC = object : HashBiMap<String, Int>() {
init {
MSG.forEach {
Expand Down Expand Up @@ -341,4 +356,14 @@ object MsgRecordUtil {
map[cache]!!
} else cache
}

fun parseNickBlocks(activeItems: List<String>): List<String> {
val items: MutableList<String> = ArrayList()
for (item in activeItems) {
if (NICK_BLOCKS.containsKey(item)) {
items.add(NICK_BLOCKS[item]!!)
}
}
return items
}
}
91 changes: 91 additions & 0 deletions app/src/main/java/cn/lliiooll/hook/AntiNickBlock.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2023 QAuxiliary developers
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/

package cn.lliiooll.hook

import cc.ioctl.util.HookUtils
import cc.ioctl.util.MsgRecordUtil
import com.github.kyuubiran.ezxhelper.utils.findMethod
import com.github.kyuubiran.ezxhelper.utils.loadClass
import com.github.kyuubiran.ezxhelper.utils.paramCount
import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify
import io.github.qauxv.util.QQVersion
import io.github.qauxv.util.requireMinQQVersion
import me.ketal.util.hookMethod
import xyz.nextalone.base.MultiItemDelayableHook
import java.text.Collator
import java.util.Locale

@FunctionHookEntry
@UiItemAgentEntry
object AntiNickBlock : MultiItemDelayableHook("ll_anti_nickblock") {
override var allItems = setOf<String>()
override val defaultItems = setOf<String>()
override var items: MutableList<String> = MsgRecordUtil.NICK_BLOCKS.keys.sortedWith(chineseSorter).toMutableList()
override val preferenceTitle: String = "屏蔽群昵称图标"
override val dialogDesc = "屏蔽群昵称图标"
override val enableCustom = false
override val uiItemLocation = Simplify.UI_CHAT_MSG


override fun initOnce(): Boolean {
val providerList = arrayOf(
"com.tencent.mobileqq.vas.vipicon.g",
"com.tencent.qqnt.aio.nick.f",
"com.tencent.mobileqq.aio.msglist.holder.component.nick.block.f",
"com.tencent.mobileqq.activity.qcircle.c",
)
val callBack = HookUtils.afterIfEnabled(this) { param ->
if (param.result != null) {
val modified = (param.result as List<*>).toMutableList()
val sources = (param.result as List<*>).toMutableList()
modified.clear()
val items: List<String> = MsgRecordUtil.parseNickBlocks(activeItems)
for (i in 0 until sources.size) {
if (!items.contains(sources[i]?.javaClass?.simpleName)) {
modified.add(sources[i])
}
}
param.result = modified.toList()
}
}

providerList.forEach { provider ->
loadClass(provider).findMethod {
//protected List<AbsNickBlock> a(@NotNull Context context, @NotNull LinearLayout rootView)
paramCount == 2
}.hookMethod(callBack)
}
return true
}

override var isEnabled: Boolean
get() = true
set(value) {}

override val isAvailable = requireMinQQVersion(QQVersion.QQ_8_9_80)

}

val chineseSorter = Comparator<String>(Collator.getInstance(Locale.CHINA)::compare)
3 changes: 2 additions & 1 deletion app/src/main/java/io/github/qauxv/util/QQVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ private QQVersion() {
public static final long QQ_8_9_78 = 4548;
public static final long QQ_8_9_80 = 4614;
public static final long QQ_8_9_83 = 4680;
public static final long qq_8_9_85 = 4766;
public static final long QQ_8_9_85 = 4766;
public static final long QQ_8_9_88 = 4852;
public static final long QQ_8_9_90 = 4938;
public static final long QQ_8_9_93_BETA_13315 = 4964;
}
4 changes: 4 additions & 0 deletions app/src/main/java/io/github/qauxv/util/dexkit/DexKitTarget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ sealed class DexKitTarget {
var descCache: String?
get() = descCacheKey.value
set(value) {
if (!value.isNullOrEmpty()) {
// check if the value is valid
DexMethodDescriptor(value)
}
descCacheKey.value = value
}

Expand Down
Loading

0 comments on commit 7924d38

Please sign in to comment.